001    /*BEGIN_COPYRIGHT_BLOCK
002     *
003     * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
004     * All rights reserved.
005     * 
006     * Redistribution and use in source and binary forms, with or without
007     * modification, are permitted provided that the following conditions are met:
008     *    * Redistributions of source code must retain the above copyright
009     *      notice, this list of conditions and the following disclaimer.
010     *    * Redistributions in binary form must reproduce the above copyright
011     *      notice, this list of conditions and the following disclaimer in the
012     *      documentation and/or other materials provided with the distribution.
013     *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014     *      names of its contributors may be used to endorse or promote products
015     *      derived from this software without specific prior written permission.
016     * 
017     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028     *
029     * This software is Open Source Initiative approved Open Source Software.
030     * Open Source Initative Approved is a trademark of the Open Source Initiative.
031     * 
032     * This file is part of DrJava.  Download the current version of this project
033     * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034     * 
035     * END_COPYRIGHT_BLOCK*/
036    
037    package edu.rice.cs.javalanglevels;
038    
039    import edu.rice.cs.javalanglevels.tree.*;
040    import edu.rice.cs.javalanglevels.parser.JExprParser;
041    import edu.rice.cs.javalanglevels.util.Utilities;
042    import java.util.*;
043    import java.io.File;
044    import edu.rice.cs.plt.reflect.JavaVersion;
045    import edu.rice.cs.plt.iter.*;
046    
047    import junit.framework.TestCase;
048    
049    /** This is a TypeChecker for all Expressions used in the students files.  It is used with every LanguageLevel. */
050    public class ExpressionTypeChecker extends SpecialTypeChecker {
051      
052      public static final JavaVersion JAVA_VERSION = LanguageLevelConverter.OPT.javaVersion();
053      public static final NullLiteral NULL_LITERAL = new NullLiteral(SourceInfo.NONE);
054      
055      /** Simply pass the necessary information on to superclass constructor.
056        * @param data  The data that represents the context.  TODO: What classes can it be?
057        * @param file  The file that corresponds to the source file
058        * @param packageName  The string representing the package name
059        * @param importedFiles  The list of file names that have been specifically imported
060        * @param importedPackages  The list of package names that have been specifically imported
061        * @param vars  The list of fields that have been assigned up to the point where SpecialTypeChecker is called.
062        * @param thrown  The list of exceptions that the context is declared to throw
063        */
064      public ExpressionTypeChecker(Data data, File file, String packageName, LinkedList<String> importedFiles, 
065                                   LinkedList<String> importedPackages, LinkedList<VariableData> vars, 
066                                   LinkedList<Pair<SymbolData, JExpression>> thrown) {
067        super(data, file, packageName, importedFiles, importedPackages, vars, thrown);
068        if (vars == null) throw new RuntimeException("vars == null in new ExpressionTypeChecker operation");
069      }
070      
071      
072      /** Visit the lhs of this assignment with LValueTypeChecker, which does extra checking.  Visit the rhs of this
073        * assignment with the regular expression type checker, since anything normal expression can appear on the right.
074        * @param that  The SimpleAssignmentExpression to type check
075        * @return  The result of the assignment.
076        */
077      public TypeData forSimpleAssignmentExpression(SimpleAssignmentExpression that) {
078        TypeData valueRes = that.getValue().visit(this);
079        TypeData nameRes = that.getName().visit(new LValueTypeChecker(this));
080        return forSimpleAssignmentExpressionOnly(that, nameRes, valueRes);
081      }
082      
083      /** A SimpleAssignmentExpression is okay if both lhs and rhs are instances, and rhs is assignable to lhs. Give an 
084        * error if these constraints are not met.  Return an instance of the lhs or null if the left or right could not be resolved.
085        * @param that  The SimpleAssignmentExpression being typechecked
086        * @param nameRes  The TypeData representing the lhs of the assignment
087        * @param valueRes  The TypeData representing the rhs of the assignment
088        */
089      public TypeData forSimpleAssignmentExpressionOnly(SimpleAssignmentExpression that, TypeData nameRes, 
090                                                        TypeData valueRes) {
091        if (nameRes == null || valueRes == null) {return null;}
092        
093        //make sure that both lhs and rhs could be resolved (not PackageDatas)
094        if (!assertFound(nameRes, that) || !assertFound(valueRes, that)) {
095          return null;
096        }
097        
098        // make sure both are instance datas
099        if (assertInstanceType(nameRes, "You cannot assign a value to the type " + nameRes.getName(), that) &&
100            assertInstanceType(valueRes, "You cannot use the type name " + valueRes.getName() + 
101                               " on the right hand side of an assignment", that)) {
102          
103          // make sure the rhs can be assigned to the lhs
104          if (! valueRes.getSymbolData().isAssignableTo(nameRes.getSymbolData(), JAVA_VERSION)) {
105            _addError("You cannot assign something of type " + valueRes.getName() + " to something of type " + 
106                      nameRes.getName(), that);
107          }
108        }   
109        return nameRes.getInstanceData();
110      }
111      
112      /** Visit the lhs of this assignment with LValueWithValueTypeChecker, which does extra checking.  Visit the rhs of
113        * this assignment with regular expression type checker, since anything regular expression can appear on the right.
114        * @param that  The PlusAssignmentExpression to type check
115        * @return  The result of the assignment.
116        */
117      public TypeData forPlusAssignmentExpression(PlusAssignmentExpression that) {
118        TypeData valueRes = that.getValue().visit(this);
119        TypeData nameRes = that.getName().visit(new LValueWithValueTypeChecker(this));
120        
121        return forPlusAssignmentExpressionOnly(that, nameRes, valueRes);
122      }
123      
124      /** A PlusAssignmentExpression is okay if the lhs and rhs are both instances.  If the lhs is a string, the rhs can
125        * be anything.  If the rhs is a string, the lhs had better be a string too.  If neither of them is a string, they 
126        * should both be numbers, and the rhs should be assignable to the lhs.
127        * @param that  The PlusAssignmentExpression we are typechecking
128        * @param nameRes  The TypeData representing the lhs
129        * @param valueRes  The TypeData representing the rhs
130        * @return  An instance of the result of the lhs, or null if either the right or the left could not be resolved.
131        */
132      public TypeData forPlusAssignmentExpressionOnly(PlusAssignmentExpression that, TypeData nameRes, 
133                                                      TypeData valueRes) {
134        if (nameRes == null || valueRes == null) {return null;}
135        
136        //make sure that both lhs and rhs could be resolved (not PackageDatas)
137        if (! assertFound(nameRes, that) || !assertFound(valueRes, that)) {
138          return null;
139        }
140        
141        // need to see if rhs is a String.
142        SymbolData string = getSymbolData("java.lang.String", that, false, false);
143        
144        if (nameRes.getSymbolData().isAssignableTo(string, JAVA_VERSION)) {
145          //the rhs is a String, so just make sure they are both instance types.
146          assertInstanceType(nameRes, "The arguments to a Plus Assignment Operator (+=) must both be instances, " + 
147                             "but you have specified a type name", that);
148          assertInstanceType(valueRes, "The arguments to a Plus Assignment Operator (+=) must both be instances, " + 
149                             "but you have specified a type name", that);
150          return string.getInstanceData();
151        }
152        
153        else { // neither is a string, so they must both be numbers
154          if (!nameRes.getSymbolData().isNumberType(JAVA_VERSION) ||
155              !valueRes.getSymbolData().isNumberType(JAVA_VERSION)) {
156            _addError("The arguments to the Plus Assignment Operator (+=) must either include an instance of a String " + 
157                      "or both be numbers.  You have specified arguments of type " + nameRes.getName() + " and " + 
158                      valueRes.getName(), that);
159            return string.getInstanceData(); // return String by default
160          }
161          
162          else if (! valueRes.getSymbolData().isAssignableTo(nameRes.getSymbolData(), 
163                                                                 JAVA_VERSION)) {
164            _addError("You cannot increment something of type " + nameRes.getName() + " with something of type " + 
165                      valueRes.getName(), that);
166          }
167          
168          else {
169            assertInstanceType(nameRes, "The arguments to the Plus Assignment Operator (+=) must both be instances, " + 
170                               "but you have specified a type name", that);
171            assertInstanceType(valueRes, "The arguments to the Plus Assignment Operator (+=) must both be instances, " + 
172                               "but you have specified a type name", that);
173          }
174          
175          return nameRes.getInstanceData();
176        }
177      }
178      
179      /** Visit the lhs of this assignment with the LValueWithValueTypeChecker, which does extra checking for the lhs, 
180        * because it needs to be able to be assigned to and already have a value.  Visit the rhs of this assignment 
181        * with the regular expression type checker, since any regular expression can appear on the right.
182        * @param that  The NumericAssignmentExpression to type check
183        * @return  The result of the assignment.
184        */
185      public TypeData forNumericAssignmentExpression(NumericAssignmentExpression that) {
186        TypeData valueRes = that.getValue().visit(this);
187        TypeData nameRes = that.getName().visit(new LValueWithValueTypeChecker(this));
188        
189        return forNumericAssignmentExpressionOnly(that, nameRes, valueRes);
190      }
191      
192      /** Delegate to method for super class. */
193      public TypeData forMinusAssignmentExpression(MinusAssignmentExpression that) {
194        return forNumericAssignmentExpression(that);
195      }
196      
197      /** Delegate to method for super class. */
198      public TypeData forMultiplyAssignmentExpression(MultiplyAssignmentExpression that) {
199        return forNumericAssignmentExpression(that);
200      }
201      
202      /** Delegate to method for super class. */
203      public TypeData forDivideAssignmentExpression(DivideAssignmentExpression that) {
204        return forNumericAssignmentExpression(that);
205      }
206      
207      /** Delegate to method for super class. */ 
208      public TypeData forModAssignmentExpression(ModAssignmentExpression that) {
209        return forNumericAssignmentExpression(that);
210      }
211      
212      /** A NumericAssignmentExpression is okay if both the lhs and the rhs are instances, both are numbers, and the rhs
213        * is assignable to the lhs. Return the lhs, or null
214        * @param that  The SimpleAssignmentExpression being typechecked
215        * @param nameRes  The TypeData representing the lhs of the assignment
216        * @param valueRes  The TypeData representing the rhs of the assignment
217        * @return  An instance of the lhs, or null if the lhs or rhs could not be resolved.
218        */
219      public TypeData forNumericAssignmentExpressionOnly(NumericAssignmentExpression that, TypeData nameRes, 
220                                                         TypeData valueRes) {
221        if (nameRes == null || valueRes == null) {return null;}
222        
223        //make sure that both lhs and rhs could be resolved (not PackageDatas)
224        if (!assertFound(nameRes, that) || !assertFound(valueRes, that)) {
225          return null;
226        }
227        
228        //make sure both are instance datas
229        if (assertInstanceType(nameRes, "You cannot use a numeric assignment (-=, %=, *=, /=) on the type " + 
230                               nameRes.getName(), that) &&
231            assertInstanceType(valueRes, "You cannot use the type name " + valueRes.getName() + 
232                               " on the left hand side of a numeric assignment (-=, %=, *=, /=)", that)) {
233          
234          boolean error = false;
235          //make sure that both lhs and rhs are number types:
236          if (!nameRes.getSymbolData().isNumberType(JAVA_VERSION)) {
237            _addError("The left side of this expression is not a number.  " + 
238                      "Therefore, you cannot apply a numeric assignment (-=, %=, *=, /=) to it", that);
239            error=true;
240          }
241          if (!valueRes.getSymbolData().isNumberType(JAVA_VERSION)) {
242            _addError("The right side of this expression is not a number.  " + 
243                      "Therefore, you cannot apply a numeric assignment (-=, %=, *=, /=) to it", that);
244            error = true;
245          }
246          
247          // Make sure the lhs is parent type of rhs  NOTE: technically this is allowable in full java but inconsistent
248          // with the fact that you cannot say int i = 0; i = i + 4.2;  To avoid student confusion, we will not allow it.
249          if (!error && !valueRes.getSymbolData().isAssignableTo(nameRes.getSymbolData(), 
250                                                                     JAVA_VERSION)) {
251            _addError("You cannot use a numeric assignment (-=, %=, *=, /=) on something of type " + nameRes.getName() + 
252                      " with something of type " + valueRes.getName(), that);
253          }
254        }  
255        return nameRes.getInstanceData();  
256      }
257      
258      /** Not currently supported. */
259      public TypeData forShiftAssignmentExpressionOnly(ShiftAssignmentExpression that, TypeData nameRes, 
260                                                       TypeData valueRes) {
261        throw new RuntimeException ("Internal Program Error: Shift assignment operators are not supported.  " + 
262                                    "This should have been caught before the TypeChecker.  Please report this bug.");
263      }
264      
265      /** Not currently supported. */
266      public TypeData forBitwiseAssignmentExpressionOnly(BitwiseAssignmentExpression that, TypeData nameRes, TypeData valueRes) {
267        throw new RuntimeException ("Internal Program Error: Bitwise assignment operators are not supported.  " + 
268                                    "This should have been caught before the TypeChecker.  Please report this bug.");
269      }
270      
271      /** Checks if this BooleanExpression is well-formed, i.e., that left and right arguments are well-formed boolean
272        * expressions.  Throws an appropriate error if ill-formed.  Always returns the boolean instance type.
273        * @param that  The BooleanExpression being checked
274        * @param left_result  The result from visiting the left side of the BooleanExpression
275        * @param right_result  The result from visiting the right side of the BooleanExpression
276        * @return  The boolean instance type
277        */
278      public TypeData forBooleanExpressionOnly(BooleanExpression that, TypeData left_result, TypeData right_result) {
279        if (left_result == null || right_result == null)  return null;
280        
281        // Make sure that both lhs and rhs could be resolved (not PackageDatas)
282        if (! assertFound(left_result, that) || ! assertFound(right_result, that)) return null;
283        
284        if (assertInstanceType(left_result, "The left side of this expression is a type, not an instance", that) &&
285            !left_result.getSymbolData().isAssignableTo(SymbolData.BOOLEAN_TYPE, JAVA_VERSION)) {
286          
287          _addError("The left side of this expression is not a boolean value.  " + 
288                    "Therefore, you cannot apply a Boolean Operator (&&, ||) to it", that);
289        }
290        
291        if (assertInstanceType(right_result, "The right side of this expression is a type, not an instance", that) &&
292            ! right_result.getSymbolData().isAssignableTo(SymbolData.BOOLEAN_TYPE, JAVA_VERSION)) {
293          
294          _addError("The right side of this expression is not a boolean value.  " + 
295                    "Therefore, you cannot apply a Boolean Operator (&&, ||) to it", that);
296        }
297        
298        
299        return SymbolData.BOOLEAN_TYPE.getInstanceData();
300      }
301      
302      /** Not currently supported. */
303      public TypeData forBitwiseBinaryExpressionOnly(BitwiseBinaryExpression that, TypeData left_result, 
304                                                     TypeData right_result) {
305        throw new RuntimeException ("Internal Program Error: Bitwise operators are not supported.  " + 
306                                    "This should have been caught before the TypeChecker.  Please report this bug.");
307      }
308      
309      /** This EqualityExpression is badly formed if left_result and right_result have incompatible types.  Both left 
310        * and the right should be instance datas.  Throws an error if ill-formed.  Returns the InstanceData corresponding
311        * to boolean, the return type from an equality check.
312        * @param that  The EqualityExpression being checked
313        * @param left_result  The result of visiting the left side of the expression
314        * @param right_result  The result of visiting the right side of the expression
315        * @return  SymbolData.BOOLEAN_TYPE.getInstanceData()
316        */
317      public TypeData forEqualityExpressionOnly(EqualityExpression that, TypeData left_result, TypeData right_result) {
318        if (left_result == null || right_result == null) return null;
319        
320        //make sure that both lhs and rhs could be resolved (not PackageDatas)
321        if (!assertFound(left_result, that) || !assertFound(right_result, that)) return null;
322        
323        //if either left or right are primitive, the must either be both numeric or both boolean
324        SymbolData left = left_result.getSymbolData();
325        SymbolData right = right_result.getSymbolData();
326        if (left.isPrimitiveType() || right.isPrimitiveType()) {
327          if (!((left.isNumberType(JAVA_VERSION) &&
328                 right.isNumberType(JAVA_VERSION)) ||
329                (left.isAssignableTo(SymbolData.BOOLEAN_TYPE, JAVA_VERSION)
330                   && right.isAssignableTo(SymbolData.BOOLEAN_TYPE, JAVA_VERSION)))) {
331            _addError("At least one of the arguments to this Equality Operator (==, !=) is primitive.  Therefore, they " + 
332                      "must either both be number types or both be boolean types.  You have specified expressions with type " +
333                      left_result.getName() + " and " + right_result.getName(), that);
334          }
335        }
336        
337        //otherwise, anything goes...just check for instance types
338        
339        assertInstanceType(left_result, "The arguments to this Equality Operator(==, !=) must both be instances.  " +
340                           "Instead, you have referenced a type name on the left side", that);
341        assertInstanceType(right_result, "The arguments to this Equality Operator(==, !=) must both be instances.  " + 
342                           "Instead, you have referenced a type name on the right side", that);
343        
344        return SymbolData.BOOLEAN_TYPE.getInstanceData();
345      }
346      
347      /** Verify that both the left and right of this comparison expression are number types and InstanceDatas.  Give an
348        * error if this is not the case.  Return the InstanceData for boolean, since that is the result of a comparison 
349        * expression.
350        * @param that  The Comparison expression being type-checked
351        * @param left_result  The result of visiting the left side of the expression
352        * @param right_result  The result of visiting the right side of the expression
353        * @return  SymbolData.BOOLEAN_TYPE.getInstanceData()
354        */
355      public TypeData forComparisonExpressionOnly(ComparisonExpression that, TypeData left_result, TypeData right_result) {
356        if (left_result == null || right_result == null) {return null;}
357        
358        //make sure that both lhs and rhs could be resolved (not PackageDatas)
359        if (!assertFound(left_result, that) || !assertFound(right_result, that)) return null;
360        
361        if (!left_result.getSymbolData().isNumberType(JAVA_VERSION)) {
362          _addError("The left side of this expression is not a number.  Therefore, you cannot apply a Comparison Operator" +
363                    " (<, >; <=, >=) to it", that);
364        }
365        else {
366          assertInstanceType(left_result, "The left side of this expression is a type, not an instance", that);
367        }
368        
369        if (!right_result.getSymbolData().isNumberType(JAVA_VERSION)) {
370          _addError("The right side of this expression is not a number.  Therefore, you cannot apply a Comparison Operator" +
371                    " (<, >; <=, >=) to it", that);
372        }
373        else {
374          assertInstanceType(right_result, "The right side of this expression is a type, not an instance", that);
375        }    
376        
377        return SymbolData.BOOLEAN_TYPE.getInstanceData();
378      }
379      
380      /**
381       * Not currently supported
382       */
383      public TypeData forShiftBinaryExpressionOnly(ShiftBinaryExpression that, TypeData left_result, TypeData right_result) {
384        throw new RuntimeException ("Internal Program Error: BinaryShifts are not supported.  " + 
385                                    "This should have been caught before the TypeChecker.  Please report this bug.");
386      }
387      
388      
389      /**
390       * A plus operator can either be used on a string and any other type of object or on two numbers.  If one of the arguments
391       * is of String type, check to make sure that both types are InstanceDatas and then return an InstanceData for String.
392       * If neither of the arguments are a String type, verify that they are both number types and both InstanceDatas, and return
393       * the Instance Data corresponding to their least restrictive type.
394       * @param that  The PlusExpression being type-checked.
395       * @param left_result  The result of visiting the left side of this plus expression
396       * @param right_result  The result of visiting the right side of this plus expression
397       */
398      public TypeData forPlusExpressionOnly(PlusExpression that, TypeData left_result, TypeData right_result) {
399        if (left_result == null || right_result == null) {return null;}
400        
401        //make sure that both lhs and rhs could be resolved (not PackageDatas)
402        if (!assertFound(left_result, that) || !assertFound(right_result, that)) {
403          return null;
404        }
405        
406        SymbolData string = getSymbolData("java.lang.String", that, false, false);
407        
408        if (left_result.getSymbolData().isAssignableTo(string, JAVA_VERSION) ||
409            right_result.getSymbolData().isAssignableTo(string, JAVA_VERSION)) {
410          //one of these is a String, so just make sure they are both instance types.
411          assertInstanceType(left_result, "The arguments to the Plus Operator (+) must both be instances, " + 
412                             "but you have specified a type name", that);
413          assertInstanceType(right_result, "The arguments to the Plus Operator (+) must both be instances, " + 
414                             "but you have specified a type name", that);
415          return string.getInstanceData();
416        }
417        
418        else { //neither is a string, so they must both be numbers
419          if (!left_result.getSymbolData().isNumberType(JAVA_VERSION) ||
420              !right_result.getSymbolData().isNumberType(JAVA_VERSION)) {
421            _addError("The arguments to the Plus Operator (+) must either include an instance of a String or both be" + 
422                      " numbers.  You have specified arguments of type " + left_result.getName() + " and " + 
423                      right_result.getName(), that);
424            return string.getInstanceData(); //return String by default
425          }
426          else {
427            assertInstanceType(left_result, "The arguments to the Plus Operator (+) must both be instances, but you have" + 
428                               " specified a type name", that);
429            assertInstanceType(right_result, "The arguments to the Plus Operator (+) must both be instances, but you have" + 
430                               " specified a type name", that);
431          }
432          
433          return _getLeastRestrictiveType(left_result.getSymbolData(), right_result.getSymbolData()).getInstanceData();
434          
435        }
436      }
437      
438      /**
439       * Check if this NumericBinaryExpression was okay.  It is not okay if either the left or the right result are not number types
440       * or if they are not instance datas.  Throw an appropriate error if any of these is the case.  Always return the least
441       * restrictive subtype of the left and the right.
442       * @param that  The NumericBinaryExpression being checked
443       * @param left_result  The result from visiting the left side of the NumericBinaryExpression
444       * @param right_result  The result from visiting the right side of the NumericBinaryExpression
445       * @return  An InstanceData of the least restrictive type of the left and right sides.
446       */
447      public TypeData forNumericBinaryExpressionOnly(NumericBinaryExpression that, TypeData left_result, TypeData right_result) {
448        if (left_result == null || right_result == null) {return null;}
449        
450        //make sure that both lhs and rhs could be resolved (not PackageDatas)
451        if (!assertFound(left_result, that) || !assertFound(right_result, that)) {
452          return null;
453        }
454        
455        if (assertInstanceType(left_result, "The left side of this expression is a type, not an instance", that) &&
456            !left_result.getSymbolData().isNumberType(JAVA_VERSION)) {
457          
458          _addError("The left side of this expression is not a number.  Therefore, you cannot apply a Numeric Binary" + 
459                    " Operator (*, /, -, %) to it", that);
460          return right_result.getInstanceData();
461        }
462        
463        if (assertInstanceType(right_result, "The right side of this expression is a type, not an instance", that) &&
464            !right_result.getSymbolData().isNumberType(JAVA_VERSION)) {
465          
466          _addError("The right side of this expression is not a number.  Therefore, you cannot apply a Numeric Binary " + 
467                    "Operator (*, /, -, %) to it", that);
468          return left_result.getInstanceData();
469        }
470        
471        
472        return _getLeastRestrictiveType(left_result.getSymbolData(), right_result.getSymbolData()).getInstanceData();
473      }
474      
475      /**
476       * This should have been caught in the first pass.  Throw a RuntimeException.
477       */
478      public TypeData forNoOpExpressionOnly(NoOpExpression that, TypeData left_result, TypeData right_result) {
479        throw new RuntimeException("Internal Program Error: The student is missing an operator.  " + 
480                                   "This should have been caught before the TypeChecker.  Please report this bug.");
481      }
482      
483      
484      /**
485       * Visit the value of this increment expression with the LValueWithValueTypeChecker, since
486       * whatever it represents should already have a value before we try to increment it.
487       */
488      public TypeData forIncrementExpression(IncrementExpression that) {
489        TypeData valueRes = that.getValue().visit(new LValueWithValueTypeChecker(this));
490        return forIncrementExpressionOnly(that, valueRes);
491      }
492      
493      
494      /**
495       * For these concrete instantiations of IncrementExpression, delegate to abstract method
496       */
497      public TypeData forPositivePrefixIncrementExpression(PositivePrefixIncrementExpression that) {
498        return forIncrementExpression(that);
499      }
500      
501      public TypeData forNegativePrefixIncrementExpression(NegativePrefixIncrementExpression that) {
502        return forIncrementExpression(that);
503      }
504      
505      public TypeData forPositivePostfixIncrementExpression(PositivePostfixIncrementExpression that) {
506        return forIncrementExpression(that);
507      }
508      
509      public TypeData forNegativePostfixIncrementExpression(NegativePostfixIncrementExpression that) {
510        return forIncrementExpression(that);
511      }
512      
513      
514      /** An IncrementExpression is badly formatted if the thing being incremented is a type (valueRes is not an 
515        * InstanceData) or if the value being incremented cannot be assigned to.  Throw an error in either of these cases.
516        * @param that  The IncrementExpression that is being type checked.
517        * @param valueRes  The result of evaluating the argument to the increment expression.
518        * @return  The type of what is being incremented.
519        */
520      public TypeData forIncrementExpressionOnly(IncrementExpression that, TypeData valueRes) {
521        if (valueRes == null) {return null;}
522        
523        //make sure that lhs could be resolved (not PackageData)
524        if (!assertFound(valueRes, that)) {
525          return null;
526        }
527        
528        if (assertInstanceType(valueRes, "You cannot increment or decrement " + valueRes.getName() + 
529                               ", because it is a class name not an instance", that)) {
530          if (!valueRes.getSymbolData().isNumberType(JAVA_VERSION)) {
531            _addError("You cannot increment or decrement something that is not a number type." + 
532                      "  You have specified something of type " + valueRes.getName(), that);
533          }
534        }
535        return valueRes.getInstanceData();
536      }
537      
538      /** A NumericUnaryExpression was well-formed if its valueRes is an instance type and if its valueRes's symbol 
539        * data is a number type (to which a double can be assigned).  If this numeric unary expression was not well formed, 
540        * throw an error.
541        * @param that  The NumericUnaryExpression being evaluated
542        * @param valueRes  The result of evaluating the argument to this expression.
543        * @return  The new result of this expression.
544        */
545      public TypeData forNumericUnaryExpressionOnly(NumericUnaryExpression that, TypeData valueRes) {
546        if (valueRes==null) {return null;}
547        
548        //make sure that lhs could be resolved (not PackageData)
549        if (!assertFound(valueRes, that)) {
550          return null;
551        }
552        
553        if (assertInstanceType(valueRes, "You cannot use a numeric unary operator (+, -) with " + valueRes.getName() + 
554                               ", because it is a class name, not an instance", that) &&
555            !valueRes.getSymbolData().isNumberType(JAVA_VERSION)) {
556          
557          _addError("You cannot apply this unary operator to something of type " + valueRes.getName() + 
558                    ".  You can only apply it to a numeric type such as double, int, or char", that);
559          return valueRes;
560        }
561        
562        //call this so that chars and bytes are widened to an int.
563        return _getLeastRestrictiveType(valueRes.getSymbolData(), SymbolData.INT_TYPE).getInstanceData();
564      }
565      
566      /** Not Currently Supported. */
567      public TypeData forBitwiseNotExpressionOnly(BitwiseNotExpression that, TypeData valueRes) {
568        throw new RuntimeException("Internal Program Error: BitwiseNot is not supported.  " + 
569                                   "It should have been caught before getting to the TypeChecker.  Please report this bug.");
570      }
571      
572      
573      /** A NotExpression is illformed if its argument is not an instance type or its argument is not of type boolean.  Give
574        * an error if this is the case.  Always return SymbolData.BOOLEAN_TYPE.getInstanceData() since this is the correct 
575        * type for this expression.
576        * @param that  The NotExpression being type-checked
577        * @param valueRes  The type of the argument to the NotExpression
578        * @return  SymbolData.BOOLEAN_TYPE.getInstanceData()
579        */
580      public TypeData forNotExpressionOnly(NotExpression that, TypeData valueRes) {
581        if (valueRes == null) {return null;}
582        
583        //make sure that lhs could be resolved (not PackageData)
584        if (!assertFound(valueRes, that)) {
585          return null;
586        }
587        
588        if (assertInstanceType(valueRes, 
589                               "You cannot use the not (!) operator with " + valueRes.getName() + 
590                               ", because it is a class name, not an instance", that) &&
591            ! valueRes.getSymbolData().isAssignableTo(SymbolData.BOOLEAN_TYPE, 
592                                                          JAVA_VERSION)) {
593          
594          _addError("You cannot use the not (!) operator with something of type " + valueRes.getName() + 
595                    ". Instead, it should be used with an expression of boolean type", that);
596        }
597        
598        return SymbolData.BOOLEAN_TYPE.getInstanceData(); //it should always be a boolean type.
599        
600      }
601      
602      /** Not currently supported */
603      public TypeData forConditionalExpressionOnly(ConditionalExpression that, TypeData condition_result, 
604                                                   TypeData forTrue_result, TypeData forFalse_result) {
605        throw new RuntimeException ("Internal Program Error: Conditional expressions are not supported.  " + 
606                                    "This should have been caught before the TypeChecker.  Please report this bug.");
607      }
608      
609      /** Checks to see if this InstanceofExpression is okay.  It is not okay if typeRes is not a SymbolData, 
610        * valueRes is not an InstanceData, or if valueRes cannot be cast to typeRes.  If any of these are true,
611        * give an appropriate error message.  Return an instance data corresponding to typeRes.
612        * @param that  The CastExpression being examined.
613        * @param typeRes  The type to be checked
614        * @param valueRes  The instance type of what is being checked
615        * @return  typeRes's instance data.
616        */
617      public TypeData forInstanceofExpressionOnly(InstanceofExpression that, TypeData typeRes, TypeData valueRes) {
618        if (typeRes == null)  return null; 
619        
620        // Make sure that lhs could be resolved (not PackageData)
621        if (! assertFound(valueRes, that) || ! assertFound(typeRes, that)) return null;
622        
623        if (typeRes.isInstanceType()) {
624          _addError("You are trying to test if an expression value belongs to an instance of a type, which is not allowed."
625                      + "  Perhaps you meant to check membership in the type itself, " + typeRes.getName(),
626                    that);
627        }
628        
629        else if (assertInstanceType(valueRes, "You are trying to test if " + valueRes.getName() + 
630                                    " belongs to type, but it is a class or interface type, not an instance", that) 
631                   && ! valueRes.getSymbolData().isCastableTo(typeRes.getSymbolData(), JAVA_VERSION)) {
632          
633          _addError("You cannot test whether an expression of type " + valueRes.getName() + " belongs to type "
634                      + typeRes.getName() + " because they are not related", 
635                    that);
636        }
637        
638        return SymbolData.BOOLEAN_TYPE.getInstanceData();
639      }
640      
641      /** Checks to see if this CastExpression is okay.  It is not okay if typeRes is not a SymbolData, valueRes is 
642        * not an InstanceData, or if valueRes cannot be cast to typeRes.  If any of these are the case, give an
643        * appropriate error message.  Return an instance data corresponding to typeRes.
644        * @param that  The CastExpression being examined.
645        * @param typeRes  The type of the cast expression
646        * @param valueRes  The instance type of what is being cast
647        * @return  typeRes's instance data.
648        */
649      public TypeData forCastExpressionOnly(CastExpression that, TypeData typeRes, TypeData valueRes) {
650        if (typeRes == null || valueRes == null)  return null; 
651        
652        //make sure that lhs could be resolved (not PackageData)
653        if (! assertFound(valueRes, that) || ! assertFound(typeRes, that)) return null;
654        
655        if (typeRes.isInstanceType()) {
656          _addError("You are trying to cast to an instance of a type, which is not allowed.  " + 
657                    "Perhaps you meant to cast to the type itself, " + typeRes.getName(), that);
658        }
659        
660        else if (assertInstanceType(valueRes, "You are trying to cast " + valueRes.getName() + 
661                                    ", which is a class or interface type, not an instance", that) &&
662                 !valueRes.getSymbolData().isCastableTo(typeRes.getSymbolData(), 
663                                                            JAVA_VERSION)) {
664          
665          _addError("You cannot cast an expression of type " + valueRes.getName() + " to type " + 
666                    typeRes.getName() + " because they are not related", that);
667        }
668        
669        return typeRes.getInstanceData();
670      }
671      
672      
673      /** Gives a Runtime Exception, because the fact that there is an EmptyExpression here should have been caught before 
674        * the TypeChecker pass.
675        */
676      public TypeData forEmptyExpressionOnly(EmptyExpression that) {
677        throw new RuntimeException("Internal Program Error: EmptyExpression encountered.  Student is missing something." + 
678                                   "  Should have been caught before TypeChecker.  Please report this bug.");
679      }
680      
681      /** Visit the ClassInstantiation's arguments.  Lookup the required constructor matching the ClassInstantiation's 
682        * argument types.  Check accessibility of the constructor.  In all cases, returns classToInstantiate.getInstanceData.
683        */
684      public InstanceData classInstantiationHelper(ClassInstantiation that, SymbolData classToInstantiate) {
685        if (classToInstantiate == null) {return null;}
686        Expression[] expr = that.getArguments().getExpressions();
687        InstanceData[] args = new InstanceData[expr.length];
688        for (int i = 0; i<expr.length; i++) {
689          Expression e = expr[i];
690          TypeData type = e.visit(this);
691          if (type == null || !assertFound(type, expr[i]) || 
692              ! assertInstanceType(type, "Cannot pass a class or interface name as a constructor argument", e)) {
693            // by default, return an instance type of context
694            return classToInstantiate.getInstanceData();
695          }
696          args[i] = type.getInstanceData();
697        }
698        
699        MethodData md = 
700          _lookupMethod(LanguageLevelVisitor.getUnqualifiedClassName(that.getType().getName()), classToInstantiate, args, 
701                        that, "No constructor found in class " + Data.dollarSignsToDots(classToInstantiate.getName()) + 
702                        " with signature: ", true, _getData().getSymbolData());
703        
704        if (md == null) {return classToInstantiate.getInstanceData();}
705        
706        //if MethodData is declared to throw exceptions, add them to thrown list:
707        String[] thrown = md.getThrown();
708        for (int i = 0; i<thrown.length; i++) {
709          _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
710        }
711        
712        return classToInstantiate.getInstanceData();
713      }
714      
715      
716      
717      
718      /** Handles a simple class instantiation.  If the type of the instantiation is not resolved, returns null because an
719        * error has already been thrown.  Also checks to see if the class being instantiated is non-static, is not a top 
720        * level class, and the name used has a dot in it.  If so, then a non-static inner class is being referenced like 
721        * a static inner class, and an error is thrown. After performing these checks, delegates to the class Instantion 
722        * helper, which will resolve the type of the class.
723        * @return  The InstanceData corresponding to the instantiation
724        */
725      public TypeData forSimpleNamedClassInstantiation(SimpleNamedClassInstantiation that) {
726        SymbolData type = getSymbolData(that.getType().getName(), _getData(), that);
727        if (type == null) {return null;}
728        // Cannot instantiate a non-static inner class from a static context (i.e. new A.B() where B is dynamic).
729        // Here, we make sure that if B is non-static, it is not an inner class of anything.
730        String name = that.getType().getName();
731        int lastIndexOfDot = name.lastIndexOf('.');
732        if (!type.hasModifier("static") && (type.getOuterData() != null) && lastIndexOfDot != -1) {
733          String firstPart = name.substring(0, lastIndexOfDot);
734          String secondPart = name.substring(lastIndexOfDot + 1, name.length()); //skip the dot itself
735          _addError(Data.dollarSignsToDots(type.getName()) + " is not a static inner class, and thus cannot be " + 
736                    "instantiated from this context.  Perhaps you meant to use an instantiation of the form new " + 
737                    firstPart + "().new " + secondPart + "()", that);
738        }
739        InstanceData result = classInstantiationHelper(that, type);
740        if (result != null && result.getSymbolData().hasModifier("abstract")) {
741          _addError(Data.dollarSignsToDots(type.getName()) + " is abstract and thus cannot be instantiated", that);
742        }
743        return result;
744      }
745      
746      /** Handles this complex named class instantiation.  First, visit the lhs and get the enclosing type.  If the 
747        * enclosing type is null, or a PackageData, return null, because an error has already been thrown.  Otherwise, 
748        * call the classInstantiationHelper to get a new instance of the rhs, from the context of the lhs.  It is an 
749        * error if the class being instantiated is non-static, but it is called from a static context.  It is an error
750        * if the class being instantiated is static but it is being called as a.new B();
751        * @param that  The ComplexNamedClassInstantiation being created
752        * @return  An InstanceData corresponding to the instantiation
753        */
754      public TypeData forComplexNamedClassInstantiation(ComplexNamedClassInstantiation that) {
755        TypeData enclosingType = that.getEnclosing().visit(this);
756        if ((enclosingType == null) || ! assertFound(enclosingType, that.getEnclosing())) { return null; }
757        
758        else {
759          //make sure we can see enclosingType
760          checkAccess(that, enclosingType.getSymbolData().getMav(), enclosingType.getSymbolData().getName(), 
761                             enclosingType.getSymbolData(), _data.getSymbolData(), "class or interface", true);
762          
763          // TODO: will getSymbolData correctly handle all cases here?
764          //TODO: We still do not handle static fields on the lhs correctly.  I think.
765          //this call to getSymbolData will throw ambiguous reference error, if appropriate
766          SymbolData innerClass = getSymbolData(that.getType().getName(), enclosingType.getSymbolData(), that.getType());
767          if (innerClass == null) {return null;}
768          
769          //make sure we can see inner class
770          checkAccess(that, innerClass.getMav(), innerClass.getName(), innerClass, _data.getSymbolData(), 
771                             "class or interface", true);
772          InstanceData result = classInstantiationHelper(that, innerClass);
773          if (result == null) {return null;}
774          boolean resultIsStatic = result.getSymbolData().hasModifier("static");
775          
776          if (!enclosingType.isInstanceType() && !resultIsStatic) {
777            _addError ("The constructor of a non-static inner class can only be called on an instance of its" + 
778                       " containing class (e.g. new " + Data.dollarSignsToDots(enclosingType.getName()) + "().new " +
779                       that.getType().getName() + "())", that);
780          }
781          else if (resultIsStatic) {
782            _addError("You cannot instantiate a static inner class or interface with this syntax.  Instead, try new " + 
783                      Data.dollarSignsToDots(result.getName()) + "()", that);
784          }
785          
786          if (result.getSymbolData().hasModifier("abstract")) {
787            _addError(Data.dollarSignsToDots(result.getName()) + " is abstract and thus cannot be instantiated", that);
788          }
789          return result;
790        }
791      }
792      
793      
794      /** Do the work that is shared between SimpleAnonymousClassInstantiation and ComplexAnonymousClassInstantiation. 
795        * Basically, update the anonymous inner class corresponding to the enclosing data and the superC with superC 
796        * and accessors, if necessary.
797        * @param that  The AnonymousClassInstantiation being processed.
798        * @param superC  The SymbolData corresponding to the super class of this instantiation (the type being created)
799        */
800      public SymbolData handleAnonymousClassInstantiation(AnonymousClassInstantiation that, SymbolData superC) {
801    //    SymbolData sd = _data.getNextAnonymousInnerClass();
802        /* The preceding line changed to following because anonymous class is filed under its enclosing class not 
803         * enclosing method. */
804        SymbolData sd = superC.getNextAnonymousInnerClass();
805    //    System.err.println("***** In handleACI(" + that.getType().getName() + ", " + superC + ") sd = " + sd);
806    //    System.err.println("Inner classes of " + superC + " are: " + superC.getInnerClasses());
807        if (sd == null) {
808          _addError("Nested anonymous classes are not supported at any language lavel", that);
809          return sd;
810    //      throw new RuntimeException("Internal Program Error: Couldn't find the SymbolData for the anonymous inner class." + 
811    //                                 "  Please report this bug.");
812        }
813        if (sd.getSuperClass() == null) {
814          if (superC == null) {
815            throw new RuntimeException("Internal Program Error:  Superclass data for " + sd + " is null." + 
816                                       "  Please report this bug.");
817          }
818          if (superC.isInterface()) {
819            sd.setSuperClass(symbolTable.get("java.lang.Object")); 
820            sd.addInterface(superC);
821          }
822          else { sd.setSuperClass(superC);}
823        }
824        LanguageLevelVisitor.createAccessors(sd, _file);
825        
826        return sd;
827      }
828      
829      /** Resolve the type of this anonymous class.  Look it up in the enclosing data, check that
830        * it is using a valid constructor through the classInstantiationHelper and visit the body.
831        * Make sure that all abstract methods are overwritten.
832        * @param that  The SimpleAnonymousClassInstantiation being type-checked
833        * @return  The result of type checking the class instantiation.
834        */
835      public TypeData forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation that) {
836        /* Note: _data should be the enclosing class. */
837    //    System.err.println("******** Type-checking the anonymous class " + that);
838    //    if (! (_data instanceof SymbolData) )
839    //      System.err.println("********* Type-checking following anon class blows up " + that);
840    //                                     
841    //    assert _data instanceof SymbolData;
842        
843        SymbolData enclosing = _data.getSymbolData();  // grabs the enclosing class if _data not already a SymbolData
844        
845        if (enclosing.isDoublyAnonymous()) {
846          _addError(enclosing + "is a nested anonymous class, which is not supported at any language level", that);
847          return null;
848        }
849    //    System.err.println("***** forSACInst called for anon class in " + enclosing);
850        final SymbolData superClass = getSymbolData(that.getType().getName(), enclosing, that); // resolve super class
851    //    System.err.println("**** SuperClass symbol is " + superClass);
852        // Get this anonymous inner class's SymbolData, and finish resolving it.
853        SymbolData myData = handleAnonymousClassInstantiation(that, enclosing /*.getEnclosingClass() */);
854    //    System.err.println("This anonymous class's symbol is: " + myData);
855        if (myData == null) return null;
856        
857        // Cannot instantiate a non-static inner class from a static context (i.e. new A.B() where B is dynamic).
858        // Here, we make sure that if B is non-static, it is not an inner class of anything.
859        String name = that.getType().getName();
860        int lastIndexOfDot = name.lastIndexOf('.');
861        if (!superClass.hasModifier("static") && !superClass.isInterface() && 
862            (superClass.getOuterData() != null) && lastIndexOfDot != -1) {
863          String firstPart = name.substring(0, lastIndexOfDot);
864          String secondPart = name.substring(lastIndexOfDot + 1, name.length());
865          _addError(Data.dollarSignsToDots(superClass.getName()) + 
866                    " is not a static inner class, and thus cannot be instantiated from this context." + 
867                    "  Perhaps you meant to use an instantiation of the form new " + Data.dollarSignsToDots(firstPart) + 
868                    "().new " + Data.dollarSignsToDots(secondPart) + "()", that);
869        }
870        
871        
872        //if superClass is an interface, then the constructor that should be used is Object--i.e. no arguments
873        if (superClass.isInterface()) {
874          Expression[] expr = that.getArguments().getExpressions();
875          if (expr.length > 0) { 
876            _addError("You are creating an anonymous inner class that directly implements an interface, thus you should" + 
877                      " use the Object constructor which takes in no arguments.  However, you have specified " + 
878                      expr.length + " arguments", that);}
879        }
880        
881        else classInstantiationHelper(that, superClass); //use super class here, since it has constructors in it
882        
883        
884        //clone the variables and visit the body.
885        LinkedList<VariableData> vars = cloneVariableDataList(_vars);
886        vars.addAll(myData.getVars());
887        final TypeData bodyRes = that.getBody().visit(new ClassBodyTypeChecker(myData, _file, _package, _importedFiles, 
888                                                                                   _importedPackages, vars, _thrown));
889        
890        
891        _checkAbstractMethods(myData, that);
892        return myData.getInstanceData();  //but actually return an instance of the anonymous inner class
893      }
894      
895      /** Resolve the type of this anonymous class.  Look it up in enclosing data, check that it is using a valid constructor
896        * through the classInstantiationHelper and visit the body. Make sure that all abstract methods are overwritten.  The
897        * enclosing data is found by first resolving the enclosing data.  Make sure that if this is an inner class it is being
898        * called from the appropriate static/non-static context (see ComplexNamedClassInstantiation for more details).
899        * @param that  The SimpleAnonymousClassInstantiation being type-checked
900        * @return  The result of type checking the class instantiation.
901        */
902      public TypeData forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation that) {
903        /* Note: _data should be the enclosing class. */
904    //    System.err.println("******** Type-checking the anonymous class " + that);
905    //    if (! (_data instanceof SymbolData) )
906    //      System.err.println("********* Type-checking following anon class blows up " + that);
907    //                                     
908    //    assert _data instanceof SymbolData;
909        
910       
911        if (_data.isDoublyAnonymous()) {
912          _addError(_data + "is a nested anonymous class, which is not supported at any language level", that);
913          return null;
914        }
915        
916        SymbolData lexEnclosing = _data.getSymbolData();  // grabs the enclosing class if _data not already a SymbolData
917        
918        Expression receiver = that.getEnclosing();
919        
920        // Get the enclosing type as specified by the "receiver" expression.
921        TypeData enclosingType = receiver.visit(this);
922        
923        if ((enclosingType == null) || ! assertFound(enclosingType, that.getEnclosing())) { return null; }
924        
925        SymbolData enclosing = enclosingType.getSymbolData();
926        
927        // Make sure we can see enclosing SymbolData from within lexEnclosing
928        checkAccess(that, enclosing.getMav(), enclosing.getName(), enclosing, lexEnclosing, "class or interface", true);
929        
930        final SymbolData superClass = getSymbolData(that.getType().getName(), enclosing, that.getType());
931        
932        // Get this anonymous inner class's SymbolData; passing lexEnclosing is a hack.  It almost certainly should be
933        // enclosing, but the LLV processing contains the same error.  We need to be consistent.
934        SymbolData myData = handleAnonymousClassInstantiation(that, lexEnclosing);  // TODO: the wrong enclosing context?
935        if (myData == null) return null;
936        
937        // TODO: will getSymbolData correctly handle all cases here?
938        //TODO: We still do not handle static fields on the lhs correctly.  I think.
939        
940        boolean resultIsStatic;
941        
942        if (superClass.isInterface()) {
943          Expression[] expr = that.getArguments().getExpressions();
944          if (expr.length > 0) { 
945            _addError("You are creating an anonymous inner class that directly implements an interface, thus you should" + 
946                      " use the Object constructor which takes in no arguments.  However, you have specified " + 
947                      expr.length + " arguments", that);
948          }
949          resultIsStatic = true;
950        }
951        
952        
953        else { // superClass is an interface...need to do some extra checking for static types.
954          InstanceData result = classInstantiationHelper(that, superClass); //use super class here, since it has constructors in it
955          if (result == null) return null;
956          
957          resultIsStatic = result.getSymbolData().hasModifier("static");
958        }
959        
960        if (!enclosingType.isInstanceType() && !resultIsStatic) {
961          _addError ("The constructor of a non-static inner class can only be called on an instance of its containing" + 
962                     " class (e.g. new " + Data.dollarSignsToDots(enclosingType.getName()) + "().new " + 
963                     that.getType().getName() + "())", that);
964        }
965        
966        else if (enclosingType.isInstanceType() && resultIsStatic) {
967          _addError("You cannot instantiate a static inner class or interface with this syntax.  Instead, try new " + 
968                    Data.dollarSignsToDots(superClass.getName()) + "()", that);
969        }
970        
971        //clone the variables and visit the body.
972        LinkedList<VariableData> vars = cloneVariableDataList(_vars);
973        vars.addAll(myData.getVars());
974        
975        final TypeData bodyRes = that.getBody().visit(new ClassBodyTypeChecker(myData, _file, _package, _importedFiles, 
976                                                                                   _importedPackages, vars, _thrown));
977        
978        //make sure all abstract super class methods are overwritten
979        _checkAbstractMethods(myData, that);
980        
981        return myData.getInstanceData(); //actually return an intance of the anonymous inner class
982      }
983      
984      
985      /** SimpleThisConstructorInvocations are not allowed outside of the first line of a constructor. */
986      public TypeData forSimpleThisConstructorInvocation(SimpleThisConstructorInvocation that) {
987        _addError("This constructor invocations are only allowed as the first statement of a constructor body", that);
988        return null;
989      }
990      
991      /** ComplexThisConstructorInvocations are not ever allowed. */
992      public TypeData forComplexThisConstructorInvocation(ComplexThisConstructorInvocation that) {
993        _addError("Constructor invocations of this form are never allowed", that);
994        return null;
995      }
996      
997      /** Try to resolve this SimpleNameReference.  It is either:
998       *    1. a field or variable reference (return the instance type of the field/variable)
999       *    2. a class or interface name reference (return the type of the class or interface)
1000       *    3. part of a package reference or an error (return a new package data corresponding to the reference.
1001       * No need to call forSimpleNameReference only, since all the checking is done here.
1002       */
1003      public TypeData forSimpleNameReference(SimpleNameReference that) {
1004        Word myWord = that.getName();
1005        myWord.visit(this);
1006        
1007        // first, try to resolve this name as a field or variable reference
1008        
1009        VariableData reference = getFieldOrVariable(myWord.getText(), _data, _data.getSymbolData(), that, _vars, true, true);
1010        if (reference != null) {
1011          if (! reference.hasValue()) {
1012            _addError("You cannot use " + reference.getName() + " because it may not have been given a value", that.getName());
1013          }
1014          
1015          // if reference is non-static (and not a local variable), but context is static, give error
1016          if (inStaticMethod() && ! reference.hasModifier("static")  && ! reference.isLocalVariable()) {
1017            _addError("Non-static variable or field " + reference.getName() + " cannot be referenced from a static context", that);
1018          }
1019    //      if (reference.getType() == null || reference.getType().getInstanceData() == null) 
1020    //        System.err.println("Expression type checking in " + _data + " blows up; AST = " + that);
1021          return reference.getType().getInstanceData();  
1022        }
1023        
1024        //next, try to resolve this name as a class or interface reference
1025        SymbolData classR = findClassReference(null, myWord.getText(), that);
1026        if (classR != null && classR != SymbolData.AMBIGUOUS_REFERENCE) {
1027          //Only return the symbolData if it is accessible--otherwise, return PackageData
1028          if (checkAccess(that, classR.getMav(), classR.getName(), classR, _data.getSymbolData(), 
1029                                 "class or interface", false)) {
1030            return classR;
1031          }
1032        }
1033        if (classR == SymbolData.AMBIGUOUS_REFERENCE) {return null;}
1034        
1035        PackageData packageD = new PackageData(myWord.getText());
1036        return packageD;
1037      }
1038      
1039      
1040      /** To resolve this ComplexNameReference, first visit the lhs with an instance of this visitor in order to get its
1041        * type.  Then, try to figure out how the name reference on the right fits with the type on the left.
1042        *   1. If the lhs is a package data, then either the rhs is a class reference, or the whole thing is another 
1043        *      PackageData.
1044        *   2. If the rhs is a variable or field visible from the context of the lhs, it must be static if the lhs is a 
1045        *      SymbolData, and regardless, it must have a value to be referenced here.
1046        *   3. If the rhs references an inner class of the lhs, the lhs must be a SymbolData if the rhs is a static inner
1047        *      class, and the rhs must be static if the lhs is a SymbolData.
1048        *   4. Otherwise, give an error because we couldn't resolve the symbol.
1049        */
1050      public TypeData forComplexNameReference(ComplexNameReference that) {
1051        TypeData lhs = that.getEnclosing().visit(this);
1052        if (lhs == null) return null;   // defensive code based on NullPointerException that MAY be due to lhs == null
1053        
1054        Word myWord = that.getName();
1055        
1056        //if lhs is a package data, either we found a class reference or this piece is still part of the package
1057        if (lhs instanceof PackageData) {
1058          SymbolData classRef =  findClassReference(lhs, myWord.getText(), that);
1059          if (classRef != null) { return classRef; }
1060          return new PackageData((PackageData) lhs, myWord.getText());
1061        }
1062        if (_data == null) return null;  // intermittent NullPointerException in next line; lhs == null or _data == null
1063        checkAccess(that, lhs.getSymbolData().getMav(), lhs.getSymbolData().getName(), lhs.getSymbolData(), 
1064                           _data.getSymbolData(), "class or interface", true);
1065        
1066        // if the word is a variable reference, make sure it can be seen from this context
1067        VariableData reference = getFieldOrVariable(myWord.getText(), lhs.getSymbolData(), _data.getSymbolData(), that);
1068        if (reference != null) {
1069          if (lhs instanceof SymbolData) {
1070            //does this reference a field? if so, it must be static
1071            if (! reference.hasModifier("static")) {
1072              _addError("Non-static variable " + reference.getName() + " cannot be accessed from the static context " + 
1073                        Data.dollarSignsToDots(lhs.getName()) + ".  Perhaps you meant to instantiate an instance of " + 
1074                        Data.dollarSignsToDots(lhs.getName()), that);
1075              return reference.getType().getInstanceData();
1076            }
1077          }
1078          
1079          //make sure it already had a value
1080          if (!reference.hasValue()) {
1081            _addError("You cannot use " + reference.getName() + " here, because it may not have been given a value", 
1082                      that.getName());
1083          }
1084          
1085          return reference.getType().getInstanceData();
1086        }
1087        
1088        //does this reference an inner class? if so, it must be static
1089        SymbolData sd = getSymbolData(true, myWord.getText(), lhs.getSymbolData(), that, false); // may report error below
1090        if (sd != null && sd != SymbolData.AMBIGUOUS_REFERENCE) {
1091          if (!checkAccess(that, sd.getMav(), sd.getName(), sd, _data.getSymbolData(), "class or interface")) {
1092            return null;
1093          }
1094          if (!sd.hasModifier("static")) {
1095            _addError("Non-static inner class " + Data.dollarSignsToDots(sd.getName()) + 
1096                      " cannot be accessed from this context.  Perhaps you meant to instantiate it", that);
1097          }
1098          
1099          //you cannot reference static inner classes from the context of an instantiation of their outer class
1100          else if (lhs instanceof InstanceData) {
1101            _addError("You cannot reference the static inner class " + Data.dollarSignsToDots(sd.getName()) + 
1102                      " from an instance of " + Data.dollarSignsToDots(lhs.getName()) + ".  Perhaps you meant to say " 
1103                        + Data.dollarSignsToDots(sd.getName()), that);
1104          }
1105          return sd;
1106        }
1107        
1108        if (sd != SymbolData.AMBIGUOUS_REFERENCE) { 
1109          _addError("Could not resolve " + myWord.getText() + " from the context of " + Data.dollarSignsToDots(lhs.getName()),
1110                    that);
1111        }
1112        return null;
1113      }
1114      
1115      
1116      /**
1117       * Make sure we are in a non-static context.
1118       * @return an instance data corresponding to the enclosing class of this context.
1119       */
1120      public TypeData forSimpleThisReference(SimpleThisReference that) {
1121        if (inStaticMethod()) {
1122          _addError("'this' cannot be referenced from within a static method", that);
1123        }
1124        return _getData().getSymbolData().getInstanceData();
1125      }
1126      
1127      /**
1128       * Check to make sure that the enclosing result could be resolved and that it a type name.
1129       * Insure that an enclosing instance of that name exists in the current (non-static) context.
1130       * Return the instance data corresponding to its "this" field.
1131       * @param that  The ComplexThisReference we are type-checking
1132       * @param enclosing_result  The TypeData whose this field is being referenced
1133       * @return  An InstanceData corresponding to the enclosing_result.
1134       */
1135      public TypeData forComplexThisReferenceOnly(ComplexThisReference that, TypeData enclosing_result) {
1136        //make sure that enclosingResult is not null and not a PackageData.  If it is, return null
1137        if ((enclosing_result == null) || ! assertFound(enclosing_result, that.getEnclosing())) { return null; }
1138        
1139        if (inStaticMethod()) {
1140          _addError("'this' cannot be referenced from within a static method", that);
1141        }
1142        
1143        if (enclosing_result.isInstanceType()) {
1144          _addError("'this' can only be referenced from a type name, but you have specified an instance of that type.", that);
1145        }
1146        
1147        SymbolData myData = _getData().getSymbolData();
1148        if (!myData.isInnerClassOf(enclosing_result.getSymbolData(), true)) {
1149          // Test whether myData is an inner class of enclosing_result at all.  Somewhat inefficient, but only happens when errors occur.
1150          if (myData.isInnerClassOf(enclosing_result.getSymbolData(), false)) {
1151            _addError("You cannot reference " + enclosing_result.getName() + ".this from here, because " + myData.getName() + 
1152                      " or one of its enclosing classes " +
1153                      "is static.  Thus, an enclosing instance of " + enclosing_result.getName() + " does not exist", that);
1154          }
1155          else {
1156            _addError("You cannot reference " + enclosing_result.getName() + ".this from here, because " + enclosing_result.getName() + 
1157                      " is not an outer class of " + myData.getName(), that);
1158          }
1159        }
1160        
1161        return enclosing_result.getInstanceData();
1162      }
1163      
1164      /** All classes should have a super class, which is java.lang.Object by default.  Looks up this class's super class.
1165        * If it is null, generates an error (this should never happen).  Otherwise, returns the instance data corresponding
1166        * to the super class.
1167        * @param that  The SimpleSuperReference we are resolving.
1168        * @return  InstanceData corresponding to the super class
1169        */
1170      public TypeData forSimpleSuperReference(SimpleSuperReference that) {
1171        if (inStaticMethod()) {
1172          _addError("'super' cannot be referenced from within a static method", that);
1173        }
1174        SymbolData superClass = _getData().getSymbolData().getSuperClass();
1175        if (superClass == null) {  //this should never happen, because all classes should have a super class
1176          _addError("The class " + _getData().getSymbolData().getName() + " does not have a super class", that);
1177          return null;
1178        }
1179        return superClass.getInstanceData();
1180      }
1181      
1182      /** Makes sure that the enclosing result is not null--if it is, return null.  Insure that an 
1183        * enclosing instance of that name exists in the current (non-static) context.  Give an error if the enclosing_result
1184        * is not an instance type.  Get its super class, and return an instance data corresponding to it.
1185        * @param that  The ComplexSuperReference being typechecked
1186        * @param enclosing_result  The type of the left hand side of this reference.
1187        * @return  An InstanceData corresponding to the super class of enclosing_result.
1188        */
1189      public TypeData forComplexSuperReferenceOnly(ComplexSuperReference that, TypeData enclosing_result) {
1190        //make sure that enclosing_result is not null and not a PackageData.  If it is, return null
1191        if ((enclosing_result == null) || ! assertFound(enclosing_result, that.getEnclosing())) { return null; }
1192        
1193        if (inStaticMethod()) {
1194          _addError("'super' cannot be referenced from within a static method", that);
1195        }
1196        if (enclosing_result.isInstanceType()) {
1197          _addError("'super' can only be referenced from a type name, but you have specified an instance of that type.", that);
1198        }
1199        
1200        SymbolData myData = _getData().getSymbolData();
1201        if (!myData.isInnerClassOf(enclosing_result.getSymbolData(), true)) {
1202          // Test whether myData is an inner class of enclosing_result.  Inefficient, but only happens when errors occur.
1203          if (myData.isInnerClassOf(enclosing_result.getSymbolData(), false)) {
1204            _addError("You cannot reference " + enclosing_result.getName() + ".super from here, because " + myData.getName() + 
1205                      " or one of its enclosing classes " +
1206                      "is static.  Thus, an enclosing instance of " + enclosing_result.getName() + " does not exist", that);
1207          }
1208          else {
1209            _addError("You cannot reference " + enclosing_result.getName() + ".super from here, because " + 
1210                      enclosing_result.getName() + " is not an outer class of " + myData.getName(), that);
1211          }
1212        }
1213        
1214        SymbolData superClass = enclosing_result.getSymbolData().getSuperClass();
1215        if (superClass == null) {  //this should never happen, because all classes should have a super class
1216          _addError("The class " + enclosing_result.getName() + " does not have a super class", that);
1217          return null;
1218        }
1219        
1220        return superClass.getInstanceData();
1221      }
1222      
1223      
1224      
1225      
1226      /** Make sure the lhs is actually an array type and that the index is an int. */
1227      public TypeData forArrayAccessOnly(ArrayAccess that, TypeData lhs, TypeData index) {
1228        //if either lhs or index is null then an error has already been caught--return null
1229        if (lhs == null || index == null) {return null;}
1230        
1231        //if either lhs or index cannot be resolved, give error
1232        if (!assertFound(lhs, that) || !assertFound(index, that)) {
1233          return null;
1234        }
1235        
1236        if (assertInstanceType(lhs, "You cannot access an array element of a type name", that) &&
1237            ! (lhs.getSymbolData() instanceof ArrayData)) {
1238          _addError("The variable referred to by this array access is a " + lhs.getSymbolData().getName() + ", not an array",
1239                    that);
1240          return lhs.getInstanceData();
1241        }
1242        
1243        if (assertInstanceType(index, "You have used a type name in place of an array index", that) &&
1244            ! index.getSymbolData().isAssignableTo(SymbolData.INT_TYPE, JAVA_VERSION)) {
1245          _addError("You cannot reference an array element with an index of type " + index.getSymbolData().getName() + 
1246                    ".  Instead, you must use an int", that);
1247        }
1248        
1249        return ((ArrayData)lhs.getSymbolData()).getElementType().getInstanceData();
1250      }
1251      
1252      //*** Primitives and Literals *******//
1253      public TypeData forStringLiteralOnly(StringLiteral that) {
1254        assert symbolTable.get("java.lang.String") != null;
1255        return symbolTable.get("java.lang.String").getInstanceData();
1256      }
1257      
1258      public TypeData forIntegerLiteralOnly(IntegerLiteral that) {
1259        return SymbolData.INT_TYPE.getInstanceData();//forLiteralOnly(that);
1260      }
1261      
1262      public TypeData forLongLiteralOnly(LongLiteral that) {
1263        return SymbolData.LONG_TYPE.getInstanceData();
1264      }
1265      
1266      public TypeData forFloatLiteralOnly(FloatLiteral that) {
1267        return SymbolData.FLOAT_TYPE.getInstanceData();
1268      }
1269      
1270      public TypeData forDoubleLiteralOnly(DoubleLiteral that) {
1271        return SymbolData.DOUBLE_TYPE.getInstanceData();
1272      }
1273      
1274      public TypeData forCharLiteralOnly(CharLiteral that) {
1275        return SymbolData.CHAR_TYPE.getInstanceData();
1276      }
1277      
1278      public TypeData forBooleanLiteralOnly(BooleanLiteral that) {
1279        return SymbolData.BOOLEAN_TYPE.getInstanceData();
1280      }
1281      
1282      public TypeData forNullLiteralOnly(NullLiteral that) {
1283        return SymbolData.NULL_TYPE.getInstanceData();
1284      }
1285      
1286      public TypeData forClassLiteralOnly(ClassLiteral that) {
1287        return symbolTable.get("java.lang.Class").getInstanceData();
1288      }
1289      
1290      
1291      /**
1292       * Check a few constraints on this Parenthesized
1293       */
1294      public TypeData forParenthesizedOnly(Parenthesized that, TypeData valueRes) {
1295        if (valueRes == null) {return null;}
1296        
1297        if (!assertFound(valueRes, that.getValue())) {
1298          return null;
1299        }
1300        
1301        assertInstanceType(valueRes, "This class or interface name cannot appear in parentheses", that);
1302        return valueRes.getInstanceData();
1303      }
1304      
1305      /** Look up the method called in the method invocation within the context of the context TypeData.
1306        * Resolve all arguments to the method, and make sure they are instance datas.
1307        * If an argument is a type, the method cannot be found, or the method is called from a static context but is
1308        * not static, give appropriate error.
1309        * If the method is declared to throw any exceptions, add them to the thrown list.
1310        * @param that  The MethodInvocation we are type checking
1311        * @param context  The TypeData that should contain the method being invoked.
1312        */
1313      public TypeData methodInvocationHelper(MethodInvocation that, TypeData context) {
1314        Expression[] exprs = that.getArguments().getExpressions();
1315        TypeData[] args = new TypeData[exprs.length];
1316        InstanceData[] newArgs = new InstanceData[exprs.length];
1317        for (int i = 0; i < exprs.length; i++) {
1318          args[i] = exprs[i].visit(this);
1319          if (args[i] == null) {
1320            return null;
1321          }
1322          
1323          if (! assertFound(args[i], that)) return null;
1324          if (! args[i].isInstanceType()) {
1325            _addError("Cannot pass a class or interface name as an argument to a method." +
1326                      "  Perhaps you meant to create an instance or use " + args[i].getName() + ".class", exprs[i]);
1327          }
1328          newArgs[i]=args[i].getInstanceData();
1329          
1330        }
1331        
1332        // Pass in both sd and the current SymbolData so that lookupMethod can check
1333        // if we have access to the method from here.
1334        MethodData md = _lookupMethod(that.getName().getText(), context.getSymbolData(), newArgs, that, 
1335                                      "No method found in class " + context.getName() + " with signature: ", 
1336                                      false, _getData().getSymbolData());
1337        
1338        if (md == null)  return null;
1339        
1340        if (! context.isInstanceType() && ! md.hasModifier("static")) {
1341          _addError("Cannot access the non-static method " + md.getName() + " from a static context", that);
1342        }
1343        
1344        // If MethodData is declared to throw exceptions, add them to thrown list:
1345        String[] thrown = md.getThrown();
1346        for (int i = 0; i < thrown.length; i++) {
1347          _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
1348        }
1349        
1350        SymbolData returnType = md.getReturnType();
1351        if (returnType == null) {
1352          _addError("Internal error: the returnType for " + md + " is null", that);
1353    //      Utilities.show("****** null return type for " + md + " Receiver type is " + context + " File is " + _file 
1354    //                       + " MethodData is " + md);
1355    //      assert false;
1356          return null;
1357        }
1358        
1359        return returnType.getInstanceData();
1360      }
1361      
1362      /** Tries to match this method invocation to a method in the context.  Here, the context is the enclosing data for
1363        * where this is being invoked.
1364        * @param that  SimpleMethodInvocation we are typechecking
1365        * @return  The return type of the method, or null if method cannot be seen or found.
1366        */
1367      //TODO: We should handle static fields too!
1368      public TypeData forSimpleMethodInvocation(SimpleMethodInvocation that) {
1369        TypeData context = _getData().getSymbolData().getInstanceData();
1370        if (inStaticMethod()) context = context.getSymbolData();  // Need SymbolData for context, not instance data.
1371        return methodInvocationHelper(that, context);
1372      }
1373      
1374      /** Tries to match this method invocation to a method in the context.  Here, the context is the enclosing field of
1375        * the method invocation.
1376        * @param that  ComplexMethodInvocation we are typechecking
1377        * @return  The return type of the method, or null if method cannot be seen or found.
1378        */
1379      //TODO: We should handle static fields too!
1380      public TypeData forComplexMethodInvocation(ComplexMethodInvocation that) {
1381        TypeData context = that.getEnclosing().visit(this);
1382        if (! assertFound(context, that.getEnclosing()) || context == null)  return null;
1383        
1384        //make sure we can see enclosingType
1385        checkAccess(that, context.getSymbolData().getMav(), context.getSymbolData().getName(), 
1386                           context.getSymbolData(), _data.getSymbolData(), "class or interface", true);
1387        
1388        // This check insures that only static methods can be called from static contexts; forces rhs to be a static context.
1389        // WHICH IS WRONG.  If the method call has an explicit receiver object, this property is IRRELEVANT.g
1390    //    if (inStaticMethod()) { context = context.getSymbolData();}
1391        return methodInvocationHelper(that, context);
1392      }
1393      
1394      
1395      /** A variable data can be assigned to if it is not final or it does not have a value.
1396        * (in other words, only final variables that have already been assigned are the only type that cannot be given a value.
1397        * @param vd  The VariableData to check.
1398        */
1399      protected boolean canBeAssigned(VariableData vd) { return ! vd.isFinal() || ! vd.hasValue(); }
1400    
1401      /** Returns the least restrictive numerical type.  According to the JLS: "If an integer 
1402        * operator other than a shift operator has at least one operand of type long, then the 
1403        * operation is carried out using 64-bit precision, and the result of the numerical operator 
1404        * is of type long. If the other operand is not long, it is first widened (??5.1.4) to type 
1405        * long by numeric promotion (??5.6). Otherwise, the operation is carried out using 32-bit 
1406        * precision, and the result of the numerical operator is of type int. If either operand 
1407        * is not an int, it is first widened to type int by numeric promotion."
1408        * So, check to see if one fo the SymboLDatas is a type less restrictive than int.  If so, return that type,
1409        * otherwise return INT_TYPE.
1410        */
1411      protected SymbolData _getLeastRestrictiveType(SymbolData sd1, SymbolData sd2) {
1412        if ((sd1.isDoubleType(JAVA_VERSION) &&
1413             sd2.isNumberType(JAVA_VERSION)) ||
1414            (sd2.isDoubleType(JAVA_VERSION) &&
1415             sd1.isNumberType(JAVA_VERSION))) {
1416          return SymbolData.DOUBLE_TYPE;
1417        }
1418        else if ((sd1.isFloatType(JAVA_VERSION) &&
1419                  sd2.isNumberType(JAVA_VERSION)) ||
1420                 (sd2.isFloatType(JAVA_VERSION) &&
1421                  sd1.isNumberType(JAVA_VERSION))) {
1422          return SymbolData.FLOAT_TYPE;
1423        }
1424        else if ((sd1.isLongType(JAVA_VERSION) &&
1425                  sd2.isNumberType(JAVA_VERSION)) ||
1426                 (sd2.isLongType(JAVA_VERSION) &&
1427                  sd1.isNumberType(JAVA_VERSION))) {
1428          return SymbolData.LONG_TYPE;
1429        }
1430        else if (sd1.isBooleanType(JAVA_VERSION) &&
1431                 sd2.isBooleanType(JAVA_VERSION)) {
1432          return SymbolData.BOOLEAN_TYPE;
1433        }
1434        else return SymbolData.INT_TYPE; // NOTE: It seems like any binary operation on number types with only ints, shorts, chars, or bytes will return an int
1435      }
1436      
1437      
1438      
1439      
1440      /**
1441       * Throw runtime exception, since conditional expressions are not allowed, and this should have been caught
1442       * before the TypeChecker.
1443       */
1444      public TypeData forConditionalExpression(ConditionalExpression that) {
1445        throw new RuntimeException("Internal Program Error: Conditional expressions are not supported.  This should have been caught before the Type Checker.  Please report this bug.");
1446      }
1447      
1448      /** Try to look up the type of the instanceof, and visit the expression that is being tested.
1449        * If the type of the instanceof is null, add an error, and return null.
1450        * If what is being tested cannot be resolved, just return the type boolean to allow further type checking.
1451        * If everything is okay, call forInstanceofExpressionOnly to do other checks.
1452        * @param that  The InstanceofExpression being typeChecked
1453        * @return  The TypeData for boolean, or null
1454        */
1455      public TypeData forInstanceofExpression(InstanceofExpression that) {
1456        //this call to getSymbolData will not throw any errors, but may return null.  If null is returned, an error needs to be added.
1457        final SymbolData typeRes = getSymbolData(that.getType().getName(), _data.getSymbolData(), that.getType(), false);
1458        final TypeData valueRes = that.getValue().visit(this);
1459        
1460        if (typeRes == null) {
1461          _addError(that.getType().getName()
1462                      + " cannot appear as the type of a instanceof expression because it is not a valid type", 
1463                    that.getType());
1464          return null;
1465        }
1466        
1467        if (! assertFound(valueRes, that.getValue())) {
1468          // An error occurred type-checking the value; return the expected type to
1469          // allow type-checking to continue.
1470          return SymbolData.BOOLEAN_TYPE.getInstanceData();
1471        }
1472        
1473        // Neither typeRes nor valueRes are null.
1474        return forInstanceofExpressionOnly(that, typeRes, valueRes);
1475      }
1476      
1477      
1478      
1479      /** Try to look up the type of the cast, and visit the expression that is being cast.
1480        * If the type being cast to is null, add an error, and return null.
1481        * If what is being cast cannot be resolved, just return the expected result of the cast, to allow type checking.
1482        * If everything is okay, call forCastExpressionOnly to do other checks.
1483        * @param that  The CastExpression being typeChecked
1484        * @return  The TypeData result of the cast, or null
1485        */
1486      public TypeData forCastExpression(CastExpression that) {
1487        //this call to getSymbolData will not throw any errors, but may return null.  If null is returned, an error needs to be added.
1488        final SymbolData typeRes = getSymbolData(that.getType().getName(), _data.getSymbolData(), that.getType(), false);
1489        final TypeData valueRes = that.getValue().visit(this);
1490        
1491        if (typeRes == null) {
1492          _addError(that.getType().getName() + " cannot appear as the type of a cast expression because it is not a valid type", that.getType());
1493          return null;
1494        }
1495        
1496        if (valueRes == null || !assertFound(valueRes, that.getValue())) {
1497          // An error occurred type-checking the value; return the expected type to
1498          // allow type-checking to continue.
1499          return typeRes.getInstanceData();
1500        }
1501        
1502        // Neither typeRes nor valueRes are null.
1503        return forCastExpressionOnly(that, typeRes, valueRes);
1504      }
1505      
1506      
1507      /**
1508       * Make sure the dimensions of the array instantiation are all instances and subtypes of int, and then return
1509       * an instance of the array.
1510       * @param that  The UninitializedArrayInstantiation being type checked
1511       * @param typeRes  The type of the array
1512       * @param dimensions_result  The array of the result of type-checking all the dimensions of this array.
1513       * @return an instance of the array.
1514       */
1515      public TypeData forUninitializedArrayInstantiationOnly(UninitializedArrayInstantiation that, TypeData typeRes, 
1516                                                             TypeData[] dimensions_result) {
1517        //make sure all of the dimensions_result dimensions are instance datas
1518        Expression[] dims = that.getDimensionSizes().getExpressions();
1519        for (int i = 0; i<dimensions_result.length; i++) {
1520          if (dimensions_result[i] != null && assertFound(dimensions_result[i], dims[i])) {
1521            if (!dimensions_result[i].getSymbolData().isAssignableTo(SymbolData.INT_TYPE, 
1522                                                                     JAVA_VERSION)) {
1523              _addError("The dimensions of an array instantiation must all be ints.  You have specified something of type " +
1524                        dimensions_result[i].getName(), dims[i]);
1525            }
1526            else {
1527              assertInstanceType(dimensions_result[i], "All dimensions of an array instantiation must be instances." + 
1528                                 "  You have specified the type " + dimensions_result[i].getName(), dims[i]);
1529            }               
1530          }
1531        }
1532        
1533        if (typeRes instanceof ArrayData) {
1534          int dim = ((ArrayData) typeRes).getDimensions();
1535          if (dimensions_result.length > dim) {
1536            //uh oh!  Dimensions list is too long!
1537            _addError("You are trying to initialize an array of type " + typeRes.getName() + " which requires " + dim +
1538                      " dimensions, but you have specified " + dimensions_result.length + " dimensions--the wrong number", 
1539                      that);
1540          }
1541        }
1542        
1543        //return an instance of the new type
1544        if (typeRes == null || !assertFound(typeRes, that)) {return null;}
1545        return typeRes.getInstanceData();
1546      }
1547      
1548      /**
1549       * Resolve the type of the array and visit its dimensions.  Call Only method to check instances in dimensions.
1550       * @param that  The SimpleUninitializedArrayInstantiation being type-checked.
1551       * @return  The type of the array, or null if there was an error.
1552       */
1553      public TypeData forSimpleUninitializedArrayInstantiation(SimpleUninitializedArrayInstantiation that) {
1554        final SymbolData typeRes = getSymbolData(that.getType().getName(), _data.getSymbolData(), that.getType());
1555        final TypeData[] dimensions_result = makeArrayOfRetType(that.getDimensionSizes().getExpressions().length);
1556        
1557        for (int i = 0; i<that.getDimensionSizes().getExpressions().length; i++) {
1558          dimensions_result[i] = that.getDimensionSizes().getExpressions()[i].visit(this);
1559        }
1560        return forUninitializedArrayInstantiationOnly(that, typeRes, dimensions_result);
1561      }
1562      
1563      
1564      /**
1565       * This is not legal java--should have been caught before the TypeChecker.  Give a runtime exception
1566       */
1567      public TypeData forComplexUninitializedArrayInstantiation(ComplexUninitializedArrayInstantiation that) {
1568        throw new RuntimeException("Internal Program Error: Complex Uninitialized Array Instantiations are not legal Java." +
1569                                   "  This should have been caught before the Type Checker.  Please report this bug.");
1570      }
1571      
1572      
1573      /**
1574       * The array initializer needs the type of the array to ensure it is properly handled.  Because of this, we use a
1575       * helper instead of calling this method directly.
1576       */
1577      public TypeData forArrayInitializer(ArrayInitializer that) {
1578        throw new RuntimeException("Internal Program Error: forArrayInitializer should never be called, but it was." + 
1579                                   "  Please report this bug.");
1580      }
1581      
1582      /**
1583       * Lookup the type of the array instantiation, and if there are any errors with it, give them.
1584       * Then, check the array initializer.
1585       * @param that  The SimpleInitializedArrayAllocationInstantiation that is being type-checked
1586       * @return  An instance of the array
1587       */
1588      public TypeData forSimpleInitializedArrayInstantiation(SimpleInitializedArrayInstantiation that) {
1589        SymbolData typeRes = getSymbolData(that.getType().getName(), _data, that.getType());
1590        TypeData elementResult = forArrayInitializerHelper(that.getInitializer(), typeRes);
1591        if (typeRes == null) {return null;}
1592        return typeRes.getInstanceData();
1593      }
1594      
1595      /**
1596       * This is not legal java--should have been caught before the TypeChecker.  Give a runtime exception
1597       */
1598      public TypeData forComplexInitializedArrayInstantiation(ComplexInitializedArrayInstantiation that) {
1599        throw new RuntimeException("Internal Program Error: Complex Initialized Array Instantiations are not legal Java." + 
1600                                   "  This should have been caught before the Type Checker.  Please report this bug.");
1601      }
1602      
1603      
1604      // Moved from TypeChecker
1605      public TypeData forInnerClassDef(InnerClassDef that) {
1606        String className = that.getName().getText();
1607        SymbolData sd = _data.getInnerClassOrInterface(className); // className is always a qualified name
1608        // Check for cyclic inheritance
1609        if (checkForCyclicInheritance(sd, new LinkedList<SymbolData>(), that)) {
1610          return null;
1611        }
1612        final TypeData mavRes = that.getMav().visit(this);
1613        final TypeData nameRes = that.getName().visit(this);
1614        final TypeData[] typeParamRes = makeArrayOfRetType(that.getTypeParameters().length);
1615        for (int i = 0; i < that.getTypeParameters().length; i++) {
1616          typeParamRes[i] = that.getTypeParameters()[i].visit(this);
1617        }
1618        final TypeData superClass = that.getSuperclass().visit(this);
1619        final TypeData[] interfacesRes = makeArrayOfRetType(that.getInterfaces().length);
1620        for (int i = 0; i < that.getInterfaces().length; i++) {
1621          interfacesRes[i] = that.getInterfaces()[i].visit(this);
1622        }
1623        final TypeData bodyRes = that.getBody().visit(new ClassBodyTypeChecker(sd, _file, _package, _importedFiles, 
1624                                                                                   _importedPackages, _vars, _thrown));
1625    //    return forInnerClassDefOnly(that, mavRes, nameRes, typeParamRes, superClass, 
1626    //                                typeParamRes, bodyRes);
1627        return null;
1628      }
1629      
1630      /** Compares the two lists of variable datas, and if a data is in both lists, mark it as having been assigned.
1631        * @param l1  One of the lists of variable datas
1632        * @param l2  The other list of variable datas.
1633        */
1634      void reassignVariableDatas(LinkedList<VariableData> l1, LinkedList<VariableData> l2) {
1635        for (int i = 0; i<l1.size(); i++) { 
1636          if (l2.contains(l1.get(i))) l1.get(i).gotValue();
1637        }
1638      }
1639      
1640      /** Compare a list of variable datas and a list of list of variable datas.  If a variable data is in the list and in each list of the lists of lists, mark it as having been
1641        * assigned.
1642        * @param tryBlock  The list of variable datas.
1643        * @param catchBlocks  The list of list of variable datas.
1644        */
1645      void reassignLotsaVariableDatas(LinkedList<VariableData> tryBlock, LinkedList<LinkedList<VariableData>> catchBlocks) {
1646        for (int i = 0; i<tryBlock.size(); i++) {
1647          boolean seenIt = true;
1648          for (int j = 0; j<catchBlocks.size(); i++) {
1649            if (! catchBlocks.get(j).contains(tryBlock.get(i))) {seenIt = false;}
1650          }
1651          
1652          if (seenIt) {        //find the variable data in vars and give it a value!
1653            tryBlock.get(i).gotValue();
1654          }
1655        }
1656      }
1657      
1658      /**
1659       * Throw the appropriate error, based on the type of the JExpression where the exception was unchecked
1660       * @param sd  The SymbolData corresponding to the exception that is thrown
1661       * @param j  The JExpression corresponding to the context of where the exception is thrown from.
1662       */
1663      public void handleUncheckedException(SymbolData sd, JExpression j) {
1664        if (j instanceof MethodInvocation) {
1665          _addError("The method " + ((MethodInvocation)j).getName().getText() + " is declared to throw the exception " + sd.getName() + " which needs to be caught or declared to be thrown", j);
1666        }
1667        else if (j instanceof ThrowStatement) {
1668          _addError("This statement throws the exception " + sd.getName() + " which needs to be caught or declared to be thrown", j);
1669        }
1670        else if (j instanceof ClassInstantiation) {
1671          _addError("The constructor for the class " + ((ClassInstantiation)j).getType().getName() + " is declared to throw the exception " + sd.getName() + " which needs to be caught or declared to be thrown.", j);
1672        }
1673        else {
1674          throw new RuntimeException("Internal Program Error: Something besides a method invocation or throw statement threw an exception.  Please report this bug.");
1675        }
1676      }
1677      
1678      
1679      /**
1680       * Returns whether the sd is a checked exception, i.e. one that needs to be caught or declared to be thrown.
1681       * This is defined as all subclasses of java.lang.Throwable except for subclasses of java.lang.RuntimeException
1682       */
1683      public boolean isCheckedException(SymbolData sd, JExpression that) {
1684        return sd.isSubClassOf(getSymbolData("java.lang.Throwable", _data, that, false)) &&
1685          ! sd.isSubClassOf(getSymbolData("java.lang.RuntimeException", _data, that, false)) &&
1686          ! sd.isSubClassOf(getSymbolData("java.lang.Error", _data, that, false));
1687      }
1688      
1689      /**
1690       * Return true if the Exception is a checked exception yet is not caught or declared to be thrown, and false otherwise.
1691       * An exception is a checked if it does not extend either java.lang.RuntimeException or java.lang.Error,
1692       * and is not declared to be thrown by the enclosing method.
1693       * @param sd  The SymbolData of the Exception we are checking.
1694       * @param that  The JExpression passed to getSymbolData for error purposes.
1695       */
1696      public boolean isUncaughtCheckedException(SymbolData sd, JExpression that) {
1697        return isCheckedException(sd, that);
1698      }
1699      
1700      //TODO: To optimize this, should 2nd for loop be moved outside of first for loop?
1701      public TypeData forBracedBody(BracedBody that) {
1702        final TypeData[] items_result = makeArrayOfRetType(that.getStatements().length);
1703        for (int i = 0; i < that.getStatements().length; i++) {
1704          items_result[i] = that.getStatements()[i].visit(this);
1705          //walk over what has been thrown and throw an error if it contains an unchecked exception
1706          for (int j = 0; j<this._thrown.size(); j++) {
1707            if (isUncaughtCheckedException(this._thrown.get(j).getFirst(), that)) {
1708              handleUncheckedException(this._thrown.get(j).getFirst(), this._thrown.get(j).getSecond());
1709            }
1710          }
1711        }
1712        
1713        return forBracedBodyOnly(that, items_result);
1714      }
1715      
1716      /** @return true type by default. */
1717      public TypeData forEmptyForCondition(EmptyForCondition that) {
1718        return SymbolData.BOOLEAN_TYPE.getInstanceData();
1719      }
1720      
1721      /** Test class for the methods defined in the above (enclosing) class. */
1722      public static class ExpressionTypeCheckerTest extends TestCase {
1723        
1724        private ExpressionTypeChecker _etc;
1725        
1726        private SymbolData _sd1;
1727        private SymbolData _sd2;
1728        private SymbolData _sd3;
1729        private SymbolData _sd4;
1730        private SymbolData _sd5;
1731        private SymbolData _sd6;
1732        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
1733        private ModifiersAndVisibility _protectedMav = 
1734          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
1735        private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
1736        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
1737        private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
1738        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
1739        private ModifiersAndVisibility _finalPublicMav = 
1740          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final", "public"});
1741        private ModifiersAndVisibility _publicAbstractMav = 
1742          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "abstract"});
1743        private ModifiersAndVisibility _publicStaticMav = 
1744          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "static"});
1745        
1746        
1747        public ExpressionTypeCheckerTest() { this(""); }
1748        public ExpressionTypeCheckerTest(String name) { super(name); }
1749        
1750        public void setUp() {
1751          errors = new LinkedList<Pair<String, JExpressionIF>>();
1752          LanguageLevelConverter.symbolTable.clear();
1753          LanguageLevelConverter._newSDs.clear();
1754          LanguageLevelConverter.loadSymbolTable();
1755          _etc = 
1756            new ExpressionTypeChecker(null, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), 
1757                                      new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
1758          LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
1759          _etc._importedPackages.addFirst("java.lang");
1760          _sd1 = new SymbolData("i.like.monkey");
1761          _sd2 = new SymbolData("i.like.giraffe");
1762          _sd3 = new SymbolData("zebra");
1763          _sd4 = new SymbolData("u.like.emu");
1764          _sd5 = new SymbolData("");
1765          _sd6 = new SymbolData("cebu");
1766          _etc._data = _sd1;
1767        }
1768        
1769        public void testForCastExpression() {
1770          CastExpression ce = new CastExpression(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "dan"), NULL_LITERAL);
1771          
1772          // if cast type is not a valid type, casting should not be allowed
1773          assertEquals("Should return null", null, ce.visit(_etc));
1774          assertEquals("There should be one error", 1, errors.size());
1775          assertEquals("Error message should be correct", 
1776                       "dan cannot appear as the type of a cast expression because it is not a valid type", 
1777                       errors.getLast().getFirst());
1778          
1779          //if cast expression cannot be resolved, return cast type instance to allow type checking to continue
1780          CastExpression ce2 = 
1781            new CastExpression(SourceInfo.NONE,
1782                               new PrimitiveType(SourceInfo.NONE, "int"),
1783                               new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "notReal")));
1784          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ce2.visit(_etc));
1785          assertEquals("There should be 2 errors", 2, errors.size());
1786          assertEquals("Error message should be correct", "Could not resolve symbol notReal", errors.getLast().getFirst());
1787          
1788          //now, try one that should work
1789          CastExpression ce3 = new CastExpression(SourceInfo.NONE,
1790                                                  new PrimitiveType(SourceInfo.NONE, "int"),
1791                                                  new DoubleLiteral(SourceInfo.NONE, 5));
1792          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ce3.visit(_etc));
1793          assertEquals("There should still be 2 errors", 2, errors.size());
1794          
1795          
1796        }
1797        
1798        public void testForCastExpressionOnly() {
1799          SymbolData sd1 = SymbolData.DOUBLE_TYPE;
1800          SymbolData sd2 = SymbolData.BOOLEAN_TYPE;
1801          SymbolData sd3 = SymbolData.INT_TYPE;
1802          
1803          CastExpression cd = new CastExpression(SourceInfo.NONE, JExprParser.NO_TYPE, NULL_LITERAL);
1804          assertEquals("When valueRes is subtype of typeRes, return typeRes.", sd1.getInstanceData(), 
1805                       _etc.forCastExpressionOnly(cd, sd1, sd3.getInstanceData()));
1806          assertEquals("Should not throw an error.", 0, errors.size());
1807          assertEquals("When typeRes is subtype of valueRes, return typeRes.", sd3.getInstanceData(), 
1808                       _etc.forCastExpressionOnly(cd, sd3, sd1.getInstanceData()));
1809          assertEquals("Should not throw an error.", 0, errors.size());
1810          assertEquals("When typeRes and valueRes are not subtypes of each other, return typeRes", 
1811                       sd2.getInstanceData(), _etc.forCastExpressionOnly(cd, sd2, sd1.getInstanceData()));
1812          assertEquals("Should now be one error.", 1, errors.size());
1813          assertEquals("Error message should be correct.", "You cannot cast an expression of type " + sd1.getName() 
1814                         + " to type " + sd2.getName() + " because they are not related", 
1815                       errors.getLast().getFirst());     
1816          SymbolData foo = new SymbolData("Foo");
1817          SymbolData fooMama = new SymbolData("FooMama");
1818          foo.setSuperClass(fooMama);
1819          assertEquals("When valueRes is a SymbolData, return typeRes", fooMama.getInstanceData(), 
1820                       _etc.forCastExpressionOnly(cd, fooMama, foo));
1821          assertEquals("There should be 2 errors.", 2, errors.size());
1822          assertEquals("Error message should be correct.", 
1823                       "You are trying to cast Foo, which is a class or interface type, not an instance.  " 
1824                         + "Perhaps you meant to create a new instance of Foo",
1825                       errors.getLast().getFirst());
1826        }
1827        
1828        public void testForEmptyExpressionOnly() {
1829          EmptyExpression ee = new EmptyExpression(SourceInfo.NONE);
1830          try {
1831            _etc.forEmptyExpressionOnly(ee);
1832            fail("Should have thrown exception");
1833          }
1834          catch (RuntimeException e) {
1835            assertEquals("Error message should be correct", 
1836                         "Internal Program Error: EmptyExpression encountered.  Student is missing something.  "
1837                           + "Should have been caught before TypeChecker.  Please report this bug.", 
1838                         e.getMessage());
1839          }
1840        }
1841        
1842        public void test_getLeastRestrictiveType() {
1843          // Assumes both number types
1844          assertEquals("Should return double.", SymbolData.FLOAT_TYPE, 
1845                       _etc._getLeastRestrictiveType(SymbolData.INT_TYPE, SymbolData.FLOAT_TYPE));
1846          assertEquals("Should return double.", SymbolData.FLOAT_TYPE, 
1847                       _etc._getLeastRestrictiveType(SymbolData.FLOAT_TYPE, SymbolData.FLOAT_TYPE));
1848          assertEquals("Should return int.", SymbolData.INT_TYPE, 
1849                       _etc._getLeastRestrictiveType(SymbolData.INT_TYPE, SymbolData.CHAR_TYPE));
1850          assertEquals("Should return char.", SymbolData.INT_TYPE, 
1851                       _etc._getLeastRestrictiveType(SymbolData.CHAR_TYPE, SymbolData.CHAR_TYPE));
1852        }
1853        
1854        public void test_isAssignableFrom() {
1855          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.DOUBLE_TYPE));
1856          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE));
1857          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.CHAR_TYPE));
1858          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.INT_TYPE, SymbolData.INT_TYPE));
1859          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.INT_TYPE, SymbolData.CHAR_TYPE));
1860          assertTrue("Should be assignable.", _etc._isAssignableFrom(SymbolData.CHAR_TYPE, SymbolData.CHAR_TYPE));
1861          
1862          _sd2.setSuperClass(_sd1);
1863          assertTrue("Should be assignable.", _etc._isAssignableFrom(_sd1, _sd1));
1864          assertTrue("Should be assignable.", _etc._isAssignableFrom(_sd1, _sd2));
1865        }
1866        
1867        
1868        //for expressions we want to check, but don't fit neatly into a category
1869        public void testRandomExpressions() {
1870          //a string of + and - before a number
1871          PositiveExpression pe = new PositiveExpression(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5));
1872          PositiveExpression pe2 = new PositiveExpression(SourceInfo.NONE, pe);
1873          NegativeExpression pe3 = new NegativeExpression(SourceInfo.NONE, pe2);
1874          PositiveExpression pe4 = new PositiveExpression(SourceInfo.NONE, pe3);
1875          PositiveExpression pe5 = new PositiveExpression(SourceInfo.NONE, pe4);
1876          NegativeExpression pe6 = new NegativeExpression(SourceInfo.NONE, pe5);
1877          
1878          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pe6.visit(_etc));
1879          assertEquals("Should be no errors", 0, errors.size());
1880        }
1881        
1882        public void testForSimpleUninitializedArrayInstantiation() {
1883          LanguageLevelVisitor llv = 
1884            new LanguageLevelVisitor(_etc._file, 
1885                                     _etc._package,
1886                                     null, // enclosingClassName for top level traversal
1887                                     _etc._importedFiles, 
1888                                     _etc._importedPackages, 
1889                                     new HashSet<String>(), 
1890                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1891                                     new LinkedList<Command>());
1892    //      LanguageLevelConverter.symbolTable = llv.symbolTable = _etc.symbolTable;
1893    //      LanguageLevelConverter._newSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
1894          
1895          SourceInfo si = SourceInfo.NONE;
1896          
1897          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
1898          intArray.setIsContinuation(false);
1899          symbolTable.remove("int[]");
1900          symbolTable.put("int[]", intArray);
1901          
1902          ArrayData intArrayArray = new ArrayData(intArray, llv, si);
1903          intArrayArray.setIsContinuation(false);
1904          symbolTable.put("int[][]", intArrayArray);
1905          
1906          ArrayData intArray3 = new ArrayData(intArrayArray, llv, si);
1907          intArray3.setIsContinuation(false);
1908          symbolTable.put("int[][][]", intArray3);
1909          
1910          Expression i1 = new IntegerLiteral(si, 5);
1911          Expression i2 = new PlusExpression(si, new IntegerLiteral(si, 5), new IntegerLiteral(si, 7));
1912          Expression i3 = new CharLiteral(si, 'c');
1913          Expression badIndexD = new DoubleLiteral(si, 4.2);
1914          Expression badIndexL = new LongLiteral(si, 4l);
1915          
1916          // Test one that works
1917          SimpleUninitializedArrayInstantiation sa1 = 
1918            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "int[][][]", 
1919                                                                        new ArrayType(si, "int[][]", 
1920                                                                                      new ArrayType(si, "int[]", new PrimitiveType(si, "int")))), 
1921                                                      new DimensionExpressionList(si, new Expression[] {i1, i2, i3}));
1922          assertEquals("Should return instance of int[][][]", intArray3.getInstanceData(), sa1.visit(_etc));
1923          assertEquals("There should be no errors", 0, errors.size());
1924          
1925          // Test one with a bad index
1926          SimpleUninitializedArrayInstantiation sa2 = 
1927            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "int[][][]", 
1928                                                                        new ArrayType(si, "int[][]", new ArrayType(si, "int[]", new PrimitiveType(si, "int")))), 
1929                                                      new DimensionExpressionList(si, new Expression[] {i1, i2, badIndexD}));
1930          assertEquals("Should return instance of int[][][]", intArray3.getInstanceData(), sa2.visit(_etc));
1931          /* The preceding test only confirms structural equality not identity  of the result.  The TypeData equals method
1932           * has been overridden to confirm that its argument belongs to the same class as this and then perform an equals 
1933           * comparison of the only field of the argument and this. */
1934          
1935          assertEquals("There should be one error", 1, errors.size());
1936          assertEquals("The error message should be correct", 
1937                       "The dimensions of an array instantiation must all be ints.  You have specified something of type double", 
1938                       errors.getLast().getFirst());
1939          
1940          //Test one with a bad type
1941          SimpleUninitializedArrayInstantiation sa3 = 
1942            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "Jonathan[]", 
1943                                                                        new ClassOrInterfaceType(si, "Jonathan", new Type[0])), 
1944                                                      new DimensionExpressionList(si, new Expression[]{i1}));
1945          assertEquals("Should return null", null, sa3.visit(_etc));
1946          assertEquals("There should be 2 errors", 2, errors.size());
1947          assertEquals("Error message should be correct", 
1948                       "Class or variable Jonathan[] not found.", 
1949                       errors.getLast().getFirst());
1950          // Test one with wrong dimensions--too many
1951          SimpleUninitializedArrayInstantiation sa4 = 
1952            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "int[][]", 
1953                                                                        new ArrayType(si, "int[]", new PrimitiveType(si, "int"))), 
1954                                                      new DimensionExpressionList(si, new Expression[] {i1, i2, i3}));
1955          assertEquals("Should return instance of int[][]", intArrayArray.getInstanceData(), sa4.visit(_etc));
1956          assertEquals("There should be 3 errors", 3, errors.size());
1957          assertEquals("Error message should be correct", 
1958                       "You are trying to initialize an array of type int[][] which requires 2 dimensions, but you have "
1959                         + "specified 3 dimensions--the wrong number", 
1960                       errors.getLast().getFirst());
1961          // Test one with wrong dimensions--too few--should be no additional errors
1962          SimpleUninitializedArrayInstantiation sa5 = 
1963            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "int[][][]", 
1964                                                                        new ArrayType(si, "int[][]", new ArrayType(si, "int[]", 
1965                                                                                                                   new PrimitiveType(si, "int")))), 
1966                                                      new DimensionExpressionList(si, new Expression[] {i1, i2}));
1967          assertEquals("Should return instance of int[][][]", intArray3.getInstanceData(), sa5.visit(_etc));
1968          assertEquals("There should still be 3 errors", 3, errors.size());
1969          
1970          //Test one where type is not accessible
1971          intArray3.setMav(_privateMav);
1972          assertEquals("Should return instance of int[][][]", intArray3.getInstanceData(), sa1.visit(_etc));
1973          assertEquals("There should be one new error", 4, errors.size());
1974          assertEquals("Error message should be correct", 
1975                       "The class or interface int[][][] in int[][][] is private and cannot be accessed from i.like.monkey", 
1976                       errors.getLast().getFirst());
1977          intArray3.setMav(_publicMav);
1978        }
1979    
1980        public void testForComplexUninitializedArrayInstantiation() {
1981          ComplexUninitializedArrayInstantiation ca1 = new ComplexUninitializedArrayInstantiation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "my")),
1982                                                                                                  new ArrayType(SourceInfo.NONE, "type[][][]", new ArrayType(SourceInfo.NONE, "type[][]", new ArrayType(SourceInfo.NONE, "type[]", new ClassOrInterfaceType(SourceInfo.NONE, "type", new Type[0])))), 
1983                                                                                                  new DimensionExpressionList(SourceInfo.NONE, new Expression[0]));
1984          //This should always give a runtime exception
1985          try {
1986            ca1.visit(_etc);
1987            fail("Should have throw runtime exception");
1988          }
1989          catch (RuntimeException e) {
1990            assertEquals("Correct exception should have been thrown","Internal Program Error: Complex Uninitialized Array Instantiations are not legal Java.  This should have been caught before the Type Checker.  Please report this bug." , e.getMessage());
1991          }
1992        }    
1993        
1994        public void testForUninitializedArrayInstantiationOnly() {
1995          LanguageLevelVisitor llv = 
1996            new LanguageLevelVisitor(_etc._file, 
1997                                     _etc._package,
1998                                     null, // enclosingClassName for top level traversal
1999                                     _etc._importedFiles, 
2000                                     _etc._importedPackages,  
2001                                     new HashSet<String>(), 
2002                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2003                                     new LinkedList<Command>());
2004          
2005    //      LanguageLevelConverter.symbolTable = llv.symbolTable = _etc.symbolTable;
2006    //      LanguageLevelConverter._newSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
2007          
2008          SourceInfo si = SourceInfo.NONE;
2009          
2010          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
2011          intArray.setIsContinuation(false);
2012          symbolTable.remove("int[]");
2013          symbolTable.put("int[]", intArray);
2014          
2015          ArrayData intArrayArray = new ArrayData(intArray, llv, si);
2016          intArrayArray.setIsContinuation(false);
2017          symbolTable.put("int[][]", intArrayArray);
2018          
2019          ArrayData intArray3 = new ArrayData(intArrayArray, llv, si);
2020          intArray3.setIsContinuation(false);
2021          symbolTable.put("int[][][]", intArray3);
2022          
2023          // One that works--int instance index
2024          SimpleUninitializedArrayInstantiation sa1 = 
2025            new SimpleUninitializedArrayInstantiation(si, new ArrayType(si, "int[][][]", 
2026                                                                        new ArrayType(si, "int[][]", new ArrayType(si, "int[]", new PrimitiveType(si, "int")))), 
2027                                                      new DimensionExpressionList(si, new Expression[] {new NullLiteral(si), new NullLiteral(si), new NullLiteral(si)}));
2028          
2029          TypeData[] arrayInitTypes1 =
2030            new TypeData[] { SymbolData.INT_TYPE.getInstanceData(), 
2031                             SymbolData.INT_TYPE.getInstanceData(), 
2032                             SymbolData.INT_TYPE.getInstanceData()};
2033          assertEquals("Should return int[][][] instance", intArray3.getInstanceData(), 
2034                       _etc.forUninitializedArrayInstantiationOnly(sa1, intArray3, arrayInitTypes1));
2035          assertEquals("Should be no errors", 0, errors.size());
2036          
2037          //one that works--char instance index
2038          TypeData[] arrayInitTypes2 =
2039            new TypeData[] { SymbolData.INT_TYPE.getInstanceData(), 
2040                             SymbolData.INT_TYPE.getInstanceData(), 
2041                             SymbolData.CHAR_TYPE.getInstanceData()};
2042          assertEquals("Should return int[][][] instance", intArray3.getInstanceData(), 
2043                       _etc.forUninitializedArrayInstantiationOnly(sa1, intArray3, arrayInitTypes2));
2044          assertEquals("Should be no errors", 0, errors.size());
2045          
2046          // one with bad index: not instance type
2047          TypeData[] arrayInitTypes3 =
2048            new TypeData[] { SymbolData.INT_TYPE.getInstanceData(), 
2049                             SymbolData.INT_TYPE, 
2050                             SymbolData.CHAR_TYPE.getInstanceData()};
2051          assertEquals("Should return int[][][] instance", intArray3.getInstanceData(), 
2052                       _etc.forUninitializedArrayInstantiationOnly(sa1, intArray3, arrayInitTypes3));
2053          assertEquals("Should be one error", 1, errors.size());
2054          assertEquals("Error message should be correct", "All dimensions of an array instantiation must be instances.  You have specified the type int.  Perhaps you meant to create a new instance of int", errors.getLast().getFirst());
2055          
2056          // one with bad index: not int type
2057          assertEquals("Should return int[][][] instance", intArray3.getInstanceData(), _etc.forUninitializedArrayInstantiationOnly(sa1, intArray3, new TypeData[] {SymbolData.INT_TYPE.getInstanceData(), SymbolData.BOOLEAN_TYPE, SymbolData.CHAR_TYPE.getInstanceData()}));
2058          assertEquals("Should be 2 errors", 2, errors.size());
2059          assertEquals("Error message should be correct", "The dimensions of an array instantiation must all be ints.  You have specified something of type boolean" , errors.getLast().getFirst());
2060          
2061          
2062        }
2063        
2064        public void testForArrayInitializer() {
2065          ArrayInitializer ai = new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {new IntegerLiteral(SourceInfo.NONE, 2)});
2066          try {
2067            ai.visit(_etc);
2068            fail("Should have throw runtime exception");
2069          }
2070          catch(RuntimeException e) {
2071            assertEquals("Exception message should be correct", "Internal Program Error: forArrayInitializer should never be called, but it was.  Please report this bug.", e.getMessage());
2072          }
2073          
2074        }
2075        
2076        public void testForSimpleInitializedArrayInstantiation() {
2077          IntegerLiteral e1 = new IntegerLiteral(SourceInfo.NONE, 5);
2078          IntegerLiteral e2 = new IntegerLiteral(SourceInfo.NONE, 7);
2079          SimpleNameReference e3 = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int"));
2080          BooleanLiteral e4 = new BooleanLiteral(SourceInfo.NONE, true);
2081          DoubleLiteral e5 = new DoubleLiteral(SourceInfo.NONE, 4.2);
2082          CharLiteral e6 = new CharLiteral(SourceInfo.NONE, 'e');
2083          SimpleNameReference e7 = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int"));
2084          
2085          ArrayType intArrayType = new ArrayType(SourceInfo.NONE, "int[]", new PrimitiveType(SourceInfo.NONE, "int"));
2086          
2087          LanguageLevelVisitor llv = 
2088            new LanguageLevelVisitor(_etc._file, 
2089                                     _etc._package, 
2090                                     null, // enclosingClassName for top level traversal
2091                                     _etc._importedFiles, 
2092                                     _etc._importedPackages, 
2093                                     new HashSet<String>(), 
2094                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2095                                     new LinkedList<Command>());
2096          
2097          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, SourceInfo.NONE);
2098          intArray.setIsContinuation(false);
2099          symbolTable.remove("int[]");
2100          symbolTable.put("int[]", intArray);
2101          
2102          //try one that should work:
2103          InitializedArrayInstantiation good = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, intArrayType, new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e2}));
2104          assertEquals("Should return int array instance", intArray.getInstanceData(), good.visit(_etc));
2105          assertEquals("Should be no errors", 0, errors.size());
2106          
2107          //char is a subtype of int, so it can be used here
2108          good = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, intArrayType, new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e2, e6}));
2109          assertEquals("Should return int array instance", intArray.getInstanceData(), good.visit(_etc));
2110          assertEquals("Should be no errors", 0, errors.size());
2111          
2112          //lhs is not an array type
2113          InitializedArrayInstantiation bad = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "int"), new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e2}));
2114          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), bad.visit(_etc));
2115          assertEquals("Should be 1 error", 1, errors.size());
2116          assertEquals("Error message should be correct", "You cannot initialize the non-array type int with an array initializer", errors.getLast().getFirst());
2117          
2118          //one of the elements is the wrong type
2119          //boolean
2120          bad = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, intArrayType, new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e4, e2, e6}));
2121          assertEquals("Should return int array instance", intArray.getInstanceData(), bad.visit(_etc));
2122          assertEquals("Should be 2 errors", 2, errors.size());
2123          assertEquals("Error message should be correct", "The elements of this initializer should have type int but element 1 has type boolean", errors.getLast().getFirst());
2124          
2125          //double
2126          bad = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, intArrayType, new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e5, e2, e6}));
2127          assertEquals("Should return int array instance", intArray.getInstanceData(), bad.visit(_etc));
2128          assertEquals("Should be 3 errors", 3, errors.size());
2129          assertEquals("Error message should be correct", "The elements of this initializer should have type int but element 1 has type double", errors.getLast().getFirst());
2130          
2131          //cannot resolve lhs
2132          bad = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "ej"), new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e2}));
2133          assertEquals("Should return null", null, bad.visit(_etc));
2134          assertEquals("Should be 4 error", 4, errors.size());
2135          assertEquals("Error message should be correct", "Class or variable ej not found.", errors.getLast().getFirst());
2136          
2137          //one of the things in the initializer is a type name!
2138          bad = new SimpleInitializedArrayInstantiation(SourceInfo.NONE, intArrayType, new ArrayInitializer(SourceInfo.NONE, new VariableInitializerI[] {e1, e7}));
2139          assertEquals("Should return instance of int[]", intArray.getInstanceData(), bad.visit(_etc));
2140          assertEquals("Should now be 5 error messages", 5, errors.size());
2141          assertEquals("Error message should be correct", "The elements of this initializer should all be instances, but you have specified the type name int.  Perhaps you meant to create a new instance of int", errors.getLast().getFirst());
2142          
2143          
2144        }
2145        
2146        
2147        
2148        
2149        public void testForSimpleAssignmentExpressionOnly() {
2150          SimpleAssignmentExpression sae = new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 5));
2151          
2152          //if lhs is assignable to rhs, and both instances, do not give any errors
2153          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), _etc.forSimpleAssignmentExpressionOnly(sae, SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
2154          assertEquals("Should be no errors", 0, errors.size());
2155          
2156          //if either input is null, return null
2157          assertEquals("Should return null", null, _etc.forSimpleAssignmentExpressionOnly(sae, null, SymbolData.INT_TYPE));
2158          assertEquals("Should return null", null, _etc.forSimpleAssignmentExpressionOnly(sae, SymbolData.INT_TYPE, null));
2159          assertEquals("Should be no errors", 0, errors.size());
2160          
2161          //if lhs is a PackageData, give error and return null
2162          PackageData pd = new PackageData("bad_reference");
2163          assertEquals("Should return null", null, _etc.forSimpleAssignmentExpressionOnly(sae, pd, SymbolData.INT_TYPE));
2164          assertEquals("Should be 1 error", 1, errors.size());
2165          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.get(0).getFirst());
2166          
2167          
2168          //if rhs is a PackageData, give an error and return null
2169          assertEquals("Should return null", null, _etc.forSimpleAssignmentExpressionOnly(sae, SymbolData.INT_TYPE, pd));
2170          assertEquals("Should only be 1 error", 1, errors.size());  // Generated error is duplicate
2171          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.get(0).getFirst());
2172          
2173          //if rhs or lhs are not instance datas, give appropriate errors
2174          assertEquals("Should return double instance", 
2175                       SymbolData.DOUBLE_TYPE.getInstanceData(), 
2176                       _etc.forSimpleAssignmentExpressionOnly(sae, 
2177                                                              SymbolData.DOUBLE_TYPE, 
2178                                                              SymbolData.INT_TYPE.getInstanceData()));
2179          assertEquals("Should now be 2 errors", 2, errors.size());  // Generated one new error; one duplicate
2180          assertEquals("Error message should be correct", 
2181                       "You cannot assign a value to the type double.  Perhaps you meant to create a new instance of double", 
2182                       errors.get(1).getFirst());
2183          
2184          assertEquals("Should return double instance", 
2185                       SymbolData.DOUBLE_TYPE.getInstanceData(), 
2186                       _etc.forSimpleAssignmentExpressionOnly(sae, 
2187                                                              SymbolData.DOUBLE_TYPE.getInstanceData(), 
2188                                                              SymbolData.INT_TYPE));
2189          assertEquals("Should now be 3 errors", 3, errors.size());
2190          assertEquals("Error message should be correct", 
2191                       "You cannot use the type name int on the right hand side of an assignment.  " +
2192                       "Perhaps you meant to create a new instance of int", 
2193                       errors.get(2).getFirst());
2194          
2195          //if rhs cannot be assigned to lhs, give error
2196          assertEquals("Should return int instance", 
2197                       SymbolData.INT_TYPE.getInstanceData(), 
2198                       _etc.forSimpleAssignmentExpressionOnly(sae, 
2199                                                              SymbolData.INT_TYPE.getInstanceData(), 
2200                                                              SymbolData.DOUBLE_TYPE.getInstanceData()));
2201          assertEquals("Should now be 4 errors", 4, errors.size());
2202          assertEquals("Error message should be correct", 
2203                       "You cannot assign something of type double to something of type int", 
2204                       errors.get(3).getFirst());
2205          
2206        }
2207        
2208        
2209        public void testForPlusAssignmentExpressionOnly() {
2210          PlusAssignmentExpression pae = 
2211            new PlusAssignmentExpression(SourceInfo.NONE, 
2212                                         new IntegerLiteral(SourceInfo.NONE, 5), new IntegerLiteral(SourceInfo.NONE, 6));
2213          
2214          //if lhs is a string, and lhs and rhs both instances, no errors
2215          SymbolData string = new SymbolData("java.lang.String");
2216          string.setIsContinuation(false);
2217          string.setPackage("java.lang");
2218          string.setMav(_publicMav);
2219          symbolTable.put("java.lang.String", string);
2220          
2221          assertEquals("Should return string instance", 
2222                       string.getInstanceData(), 
2223                       _etc.forPlusAssignmentExpressionOnly(pae, string.getInstanceData(), 
2224                                                            SymbolData.INT_TYPE.getInstanceData()));
2225          assertEquals("Should be no errors", 0, errors.size());
2226          
2227          //if both number instances, no errors
2228          assertEquals("Should return double instance", 
2229                       SymbolData.DOUBLE_TYPE.getInstanceData(), 
2230                       _etc.forPlusAssignmentExpressionOnly(pae, 
2231                                                            SymbolData.DOUBLE_TYPE.getInstanceData(), 
2232                                                            SymbolData.INT_TYPE.getInstanceData()));
2233          assertEquals("Should be no errors", 0, errors.size());
2234          
2235          //if either input is null, return null
2236          assertEquals("Should return null", null, _etc.forPlusAssignmentExpressionOnly(pae, null, SymbolData.INT_TYPE));
2237          assertEquals("Should return null", null, _etc.forPlusAssignmentExpressionOnly(pae, SymbolData.INT_TYPE, null));
2238          assertEquals("Should be no errors", 0, errors.size());
2239          
2240          //if lhs is a PackageData, give error and return null
2241          PackageData pd = new PackageData("bad_reference");
2242          assertEquals("Should return null", null, _etc.forPlusAssignmentExpressionOnly(pae, pd, SymbolData.INT_TYPE));
2243          assertEquals("Should be 1 error", 1, errors.size());
2244          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.getLast().getFirst());
2245          
2246          
2247          //if rhs is a PackageData, give an error and return null
2248          assertEquals("Should return null", null, _etc.forPlusAssignmentExpressionOnly(pae, SymbolData.INT_TYPE, pd));
2249          assertEquals("Should be 1 error", 1, errors.size());  // Generated duplicate error message
2250          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.get(0).getFirst());
2251          
2252          //if lhs is a string, but not an instance data, give error
2253          assertEquals("Should return string instance", 
2254                       string.getInstanceData(), 
2255                       _etc.forPlusAssignmentExpressionOnly(pae, string, 
2256                                                            SymbolData.INT_TYPE.getInstanceData()));
2257          assertEquals("Should now be 2 errors", 2, errors.size());
2258          assertEquals("Error message should be correct",
2259                       "The arguments to a Plus Assignment Operator (+=) must both be instances, but you have specified " +
2260                       "a type name.  Perhaps you meant to create a new instance of java.lang.String", 
2261                       errors.get(1).getFirst());
2262          
2263          //if lhs is a string, but rhs is not an instance, give error
2264          assertEquals("Should return string instance", string.getInstanceData(), 
2265                       _etc.forPlusAssignmentExpressionOnly(pae, string.getInstanceData(), 
2266                                                            SymbolData.INT_TYPE));
2267          assertEquals("Should now be 3 errors", 3, errors.size());
2268          assertEquals("Error message should be correct",
2269                       "The arguments to a Plus Assignment Operator (+=) must both be instances, " +
2270                       "but you have specified a type name.  Perhaps you meant to create a new instance of int" , 
2271                       errors.get(2).getFirst());
2272          
2273          // if rhs is not a number or string, give error
2274          assertEquals("Should return string, by default", string.getInstanceData(), 
2275                       _etc.forPlusAssignmentExpressionOnly(pae, _sd2.getInstanceData(), 
2276                                                            SymbolData.INT_TYPE.getInstanceData()));
2277          assertEquals("Should now be 4 errors", 4, errors.size());
2278          assertEquals("Error message should be correct", 
2279                       "The arguments to the Plus Assignment Operator (+=) must either include an instance of a String " +
2280                       "or both be numbers.  You have specified arguments of type " + _sd2.getName() + " and int", 
2281                       errors.get(3).getFirst());
2282          
2283          // if rhs is number but lhs is not, give error
2284          assertEquals("should return string, by default", string.getInstanceData(),
2285                       _etc.forPlusAssignmentExpressionOnly(pae, SymbolData.INT_TYPE.getInstanceData(),
2286                                                            _sd2.getInstanceData()));
2287          assertEquals("Should now be 5 errors", 5, errors.size());  // Generated slightly different error message
2288          assertEquals("Error message should be correct", 
2289                       "The arguments to the Plus Assignment Operator (+=) must either include an instance of a String " +
2290                       "or both be numbers.  You have specified arguments of type int and " + _sd2.getName(), 
2291                       errors.get(4).getFirst());
2292          
2293          assertEquals("Should return int instance", 
2294                       SymbolData.INT_TYPE.getInstanceData(), 
2295                       _etc.forPlusAssignmentExpressionOnly(pae, SymbolData.INT_TYPE.getInstanceData(), 
2296                                                            SymbolData.DOUBLE_TYPE.getInstanceData()));
2297          assertEquals("Should now be 6 errors", 6, errors.size());
2298          assertEquals("Error message should be correct", 
2299                       "You cannot increment something of type int with something of type double", 
2300                       errors.get(5).getFirst());
2301          
2302          //if both numbers, but not instances, give errors
2303          assertEquals("Should return double instance", 
2304                       SymbolData.DOUBLE_TYPE.getInstanceData(), 
2305                       _etc.forPlusAssignmentExpressionOnly(pae, SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE));
2306          assertEquals("Should now be 8 errors", 8, errors.size());
2307          assertEquals("Second error message should be new", 
2308                       "The arguments to the Plus Assignment Operator (+=) must both be instances, but you have specified " +
2309                       "a type name.  Perhaps you meant to create a new instance of double", 
2310                       errors.get(6).getFirst());
2311          assertEquals("First error message should be new", 
2312                       "The arguments to the Plus Assignment Operator (+=) must both be instances, but you have specified " +
2313                       "a type name.  Perhaps you meant to create a new instance of int", 
2314                       errors.get(7).getFirst());
2315        }
2316        
2317        public void testForNumericAssignmentExpressionOnly() {
2318          NumericAssignmentExpression nae = 
2319            new MinusAssignmentExpression(SourceInfo.NONE, 
2320                                          new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")),
2321                                          new IntegerLiteral(SourceInfo.NONE, 5));
2322          
2323          //if both lhs and rhs are instances of numbers, and lhs is assignable to rhs, should be no errors
2324          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.INT_TYPE.getInstanceData(), SymbolData.CHAR_TYPE.getInstanceData()));
2325          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
2326          assertEquals("Should be no errors", 0, errors.size());
2327          
2328          
2329          //if either input is null, return null
2330          assertEquals("Should return null", null, _etc.forNumericAssignmentExpressionOnly(nae, null, SymbolData.INT_TYPE));
2331          assertEquals("Should return null", null, _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.INT_TYPE, null));
2332          assertEquals("Should be no errors", 0, errors.size());
2333          
2334          //if lhs is a PackageData, give error and return null
2335          PackageData pd = new PackageData("bad_reference");
2336          assertEquals("Should return null", null, _etc.forNumericAssignmentExpressionOnly(nae, pd, SymbolData.INT_TYPE));
2337          assertEquals("Should be 1 error", 1, errors.size());
2338          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.get(0).getFirst());
2339          
2340          //if rhs is a PackageData, give an error and return null
2341          assertEquals("Should return null", null, _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.INT_TYPE, pd));
2342          assertEquals("Should still be 1 error", 1, errors.size());  // Generated duplicate error message
2343          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.get(0).getFirst());
2344          
2345          //if lhs not an instance data, give error
2346          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.INT_TYPE, SymbolData.CHAR_TYPE.getInstanceData()));
2347          assertEquals("Should be 2 errors", 2, errors.size());  // Generated a duplicate error message
2348          assertEquals("Error message should be correct", 
2349                       "You cannot use a numeric assignment (-=, %=, *=, /=) on the type int.  Perhaps you meant to create " +
2350                       "a new instance of int", 
2351                       errors.get(1).getFirst());
2352          // if rhs not instance data, give error
2353          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), _etc.forNumericAssignmentExpressionOnly(nae, SymbolData.INT_TYPE.getInstanceData(), SymbolData.CHAR_TYPE));
2354          assertEquals("Should now be 3 errors", 3, errors.size());
2355          assertEquals("Error message should be correct", 
2356                       "You cannot use the type name char on the left hand side of a numeric assignment (-=, %=, *=, /=)." +
2357                       "  Perhaps you meant to create a new instance of char", 
2358                       errors.get(2).getFirst());
2359          
2360          //if lhs not a number type, give error
2361          assertEquals("Should return sd2 instance", _sd2.getInstanceData(), 
2362                       _etc.forNumericAssignmentExpressionOnly(nae, _sd2.getInstanceData(), 
2363                                                               SymbolData.CHAR_TYPE.getInstanceData()));
2364          assertEquals("Should now be 4 errors", 4, errors.size());
2365          assertEquals("Error message should be correct", 
2366                       "The left side of this expression is not a number.  Therefore, you cannot apply " + 
2367                       "a numeric assignment (-=, %=, *=, /=) to it", 
2368                       errors.get(3).getFirst());
2369          
2370          //if rhs is not a number type, give error
2371          assertEquals("Should return int instance", 
2372                       SymbolData.INT_TYPE.getInstanceData(), 
2373                       _etc.forNumericAssignmentExpressionOnly(nae, 
2374                                                               SymbolData.INT_TYPE.getInstanceData(), 
2375                                                               _sd2.getInstanceData()));
2376          assertEquals("Should still be 5 errors", 5, errors.size());  // Generated a duplicate error message
2377          assertEquals("Error message should be correct", 
2378                       "The right side of this expression is not a number.  Therefore, you cannot apply " +
2379                       "a numeric assignment (-=, %=, *=, /=) to it", 
2380                       errors.get(4).getFirst());
2381          
2382          //if rhs is not assignable to lhs, give error
2383          assertEquals("Should return int instance", 
2384                       SymbolData.INT_TYPE.getInstanceData(), 
2385                       _etc.forNumericAssignmentExpressionOnly(nae, 
2386                                                               SymbolData.INT_TYPE.getInstanceData(), 
2387                                                               SymbolData.DOUBLE_TYPE.getInstanceData()));
2388          assertEquals("Should be 6 errors", 6, errors.size());
2389          assertEquals("Error message should be correct", 
2390                       "You cannot use a numeric assignment (-=, %=, *=, /=) on something of type int with something of " +
2391                       "type double", 
2392                       errors.get(5).getFirst());
2393        }
2394        
2395        
2396        public void testForShiftAssignmentExpressionOnly() {
2397          ShiftAssignmentExpression sae = new LeftShiftAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), new IntegerLiteral(SourceInfo.NONE, 2));
2398          try {
2399            _etc.forShiftAssignmentExpressionOnly(sae, _sd1, _sd2);
2400            fail("forShiftAssignmentExpressionOnly should have thrown a runtime exception");
2401          }
2402          catch (RuntimeException e) {
2403            assertEquals("Exception message should be correct", "Internal Program Error: Shift assignment operators are not supported.  This should have been caught before the TypeChecker.  Please report this bug.", e.getMessage());
2404          }
2405        }
2406        
2407        public void testForBitwiseAssignmentExpressionOnly() {
2408          BitwiseAssignmentExpression bae = new BitwiseXorAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), new IntegerLiteral(SourceInfo.NONE, 2));
2409          try {
2410            _etc.forBitwiseAssignmentExpressionOnly(bae, _sd1, _sd2);
2411            fail("forBitwiseAssignmentExpressionOnly should have thrown a runtime exception");
2412          }
2413          catch (RuntimeException e) {
2414            assertEquals("Exception message should be correct", "Internal Program Error: Bitwise assignment operators are not supported.  This should have been caught before the TypeChecker.  Please report this bug.", e.getMessage());
2415          }
2416        }
2417        
2418        
2419        public void testForBooleanExpressionOnly() {
2420          BooleanExpression be = new OrExpression(SourceInfo.NONE, new BooleanLiteral(SourceInfo.NONE, true), new BooleanLiteral(SourceInfo.NONE, false));
2421          
2422          //if both left and right are boolean instance types, everything is good
2423          
2424          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forBooleanExpressionOnly(be, SymbolData.BOOLEAN_TYPE.getInstanceData(), SymbolData.BOOLEAN_TYPE.getInstanceData()));
2425          assertEquals("There should be no errors", 0, errors.size());
2426          
2427          //if the left type is not an instance type, give an error
2428          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forBooleanExpressionOnly(be, SymbolData.BOOLEAN_TYPE, SymbolData.BOOLEAN_TYPE.getInstanceData()));
2429          assertEquals("There should now be 1 error", 1, errors.size());
2430          assertEquals("The error message should be correct", "The left side of this expression is a type, not an instance.  Perhaps you meant to create a new instance of boolean", errors.getLast().getFirst());
2431          
2432          //if the left type is an instance type but not a boolean type, give an error
2433          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forBooleanExpressionOnly(be, SymbolData.INT_TYPE.getInstanceData(), SymbolData.BOOLEAN_TYPE.getInstanceData()));
2434          assertEquals("There should now be 2 errors", 2, errors.size());
2435          assertEquals("The error message should be correct", "The left side of this expression is not a boolean value.  Therefore, you cannot apply a Boolean Operator (&&, ||) to it", errors.getLast().getFirst());
2436          
2437          //if the right type is not an instance type, give an error
2438          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forBooleanExpressionOnly(be, SymbolData.BOOLEAN_TYPE.getInstanceData(), SymbolData.BOOLEAN_TYPE));
2439          assertEquals("There should now be 3 errors", 3, errors.size());
2440          assertEquals("The error message should be correct", "The right side of this expression is a type, not an instance.  Perhaps you meant to create a new instance of boolean", errors.getLast().getFirst());
2441          
2442          //if the right type is an instance type but not a boolean give an error
2443          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forBooleanExpressionOnly(be, SymbolData.BOOLEAN_TYPE.getInstanceData(), SymbolData.DOUBLE_TYPE.getInstanceData()));
2444          assertEquals("There should now be 4 errors", 4, errors.size());
2445          assertEquals("The error message should be correct", 
2446                       "The right side of this expression is not a boolean value.  Therefore, you cannot apply a Boolean Operator (&&, ||) to it", errors.getLast().getFirst());
2447          
2448        }
2449        
2450        public void testForBitwiseBinaryExpressionOnly() {
2451          BitwiseBinaryExpression bbe = 
2452            new BitwiseAndExpression(SourceInfo.NONE, 
2453                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), 
2454                                     new IntegerLiteral(SourceInfo.NONE, 2));
2455          try {
2456            _etc.forBitwiseBinaryExpressionOnly(bbe, _sd2, _sd3);
2457            fail("forBitwiseBinaryExpressionOnly should have thrown a runtime exception");
2458          }
2459          catch (RuntimeException e) {
2460            assertEquals("Exception message should be correct", 
2461                         "Internal Program Error: Bitwise operators are not supported.  This should have been caught "
2462                           + "before the TypeChecker.  Please report this bug.", e.getMessage());
2463          }
2464        }
2465        
2466        
2467        public void testForEqualityExpressionOnly() {
2468          EqualityExpression ee = new EqualsExpression(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
2469          
2470          //left and right are both primitive and both boolean type--should work
2471          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2472                       _etc.forEqualityExpressionOnly(ee, SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2473                                                      SymbolData.BOOLEAN_TYPE.getInstanceData()));
2474          assertEquals("Should be no errors", 0, errors.size());
2475          
2476          //left and right are both primitive and both int type--should work
2477          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2478                       _etc.forEqualityExpressionOnly(ee, SymbolData.INT_TYPE.getInstanceData(), 
2479                                                      SymbolData.INT_TYPE.getInstanceData()));
2480          assertEquals("Should be no errors", 0, errors.size());
2481          
2482          
2483          //left and right are both number types, only left is primitive--should work
2484          SymbolData integer = new SymbolData("java.lang.Integer");
2485          integer.setIsContinuation(false);
2486          symbolTable.put("java.lang.Integer", integer);
2487          
2488          SymbolData bool = new SymbolData("java.lang.Boolean");
2489          bool.setIsContinuation(false);
2490          symbolTable.put("java.lang.Boolean", bool);
2491          
2492          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2493                       _etc.forEqualityExpressionOnly(ee, SymbolData.INT_TYPE.getInstanceData(), integer.getInstanceData()));
2494          assertEquals("Should be no errors", 0, errors.size());
2495          
2496          //left and right are both number types, only right is primitive--should work
2497          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2498                       _etc.forEqualityExpressionOnly(ee, integer.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
2499          assertEquals("Should be no errors", 0, errors.size());
2500          
2501          //left and right are both boolean types, only left is primitive--should work
2502          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2503                       _etc.forEqualityExpressionOnly(ee, SymbolData.BOOLEAN_TYPE.getInstanceData(), bool.getInstanceData()));
2504          assertEquals("Should be no errors", 0, errors.size());
2505          
2506          //left and right are both boolean types, only right is primitive--should work
2507          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2508                       _etc.forEqualityExpressionOnly(ee, bool.getInstanceData(), SymbolData.BOOLEAN_TYPE.getInstanceData()));
2509          assertEquals("Should be no errors", 0, errors.size());
2510          
2511          
2512          //left and right are both instances of reference types--should work
2513          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2514                       _etc.forEqualityExpressionOnly(ee, _sd1.getInstanceData(), _sd2.getInstanceData()));
2515          assertEquals("Should be no errors", 0, errors.size());
2516          
2517          //left and right are both primitive, but one is int and one is boolean--does not work
2518          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2519                       _etc.forEqualityExpressionOnly(ee, SymbolData.INT_TYPE.getInstanceData(), 
2520                                                      SymbolData.BOOLEAN_TYPE.getInstanceData()));
2521          assertEquals("Should be 1 error", 1, errors.size());
2522          assertEquals("Error message should be correct", 
2523                       "At least one of the arguments to this Equality Operator (==, !=) is primitive.  Therefore, "
2524                         + "they must either both be number types or both be boolean types.  You have specified "
2525                         + "expressions with type int and boolean", 
2526                       errors.getLast().getFirst());
2527          
2528          //left is primitive, right is not, not both number types--does not work
2529          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), _etc.forEqualityExpressionOnly(ee, SymbolData.INT_TYPE.getInstanceData(), _sd1.getInstanceData()));
2530          assertEquals("There should now be 2 errors", 2, errors.size());
2531          assertEquals("Error message should be correct", 
2532                       "At least one of the arguments to this Equality Operator (==, !=) is primitive.  Therefore, "
2533                         + "they must either both be number types or both be boolean types.  You have specified "
2534                         + "expressions with type int and i.like.monkey", errors.getLast().getFirst());
2535          
2536          //left is not primitive, right is, not both primitives--does not work
2537          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2538                       _etc.forEqualityExpressionOnly(ee, _sd1.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
2539          assertEquals("There should now be 3 errors", 3, errors.size());
2540          assertEquals("Error message should be correct", 
2541                       "At least one of the arguments to this Equality Operator (==, !=) is primitive.  Therefore, they "
2542                         + "must either both be number types or both be boolean types.  You have specified expressions "
2543                         + "with type i.like.monkey and int", 
2544                       errors.getLast().getFirst());
2545          
2546          //neither left nor right are primitive, but left side not an instance type
2547          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2548                       _etc.forEqualityExpressionOnly(ee, _sd1, _sd2.getInstanceData()));
2549          assertEquals("There should now be 4 errors", 4, errors.size());
2550          assertEquals("Error message should be correct", 
2551                       "The arguments to this Equality Operator(==, !=) must both be instances.  Instead, you have "
2552                         +"referenced a type name on the left side.  Perhaps you meant to create a new instance of " + 
2553                       _sd1.getName(), 
2554                       errors.getLast().getFirst());
2555          
2556          //neither left nor right are primitive, but right side not an instance type
2557          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2558                       _etc.forEqualityExpressionOnly(ee, _sd1.getInstanceData(), _sd2));
2559          assertEquals("There should now be 5 errors", 5, errors.size());
2560          assertEquals("Error message should be correct", "The arguments to this Equality Operator(==, !=) must both "
2561                         + "be instances.  Instead, you have referenced a type name on the right side.  Perhaps you "
2562                         + "meant to create a new instance of " + _sd2.getName(), 
2563                       errors.getLast().getFirst());
2564        }
2565        
2566        public void testForComparisonExpressionOnly() {
2567          ComparisonExpression ce = new LessThanExpression(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
2568          
2569          //does not throw an error if both expressions are numbers and instance types
2570          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2571                       _etc.forComparisonExpressionOnly(ce, SymbolData.DOUBLE_TYPE.getInstanceData(), 
2572                                                        SymbolData.INT_TYPE.getInstanceData()));
2573          assertEquals("There should be no errors", 0, errors.size());
2574          
2575          //gives an error if left side is not a number
2576          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(),
2577                       _etc.forComparisonExpressionOnly(ce, SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2578                                                        SymbolData.INT_TYPE.getInstanceData()));
2579          assertEquals("There should be one error", 1, errors.size());
2580          assertEquals("Error message should be correct", 
2581                       "The left side of this expression is not a number.  Therefore, you cannot apply a Comparison "
2582                         + "Operator (<, >; <=, >=) to it", 
2583                       errors.getLast().getFirst());
2584          
2585          //gives an error if left side is not an instance type
2586          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2587                       _etc.forComparisonExpressionOnly(ce, SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE.getInstanceData()));
2588          assertEquals("There should be two errors", 2, errors.size());
2589          assertEquals("Error message should be correct", 
2590                       "The left side of this expression is a type, not an instance.  Perhaps you meant to create a "
2591                         + "new instance of double", 
2592                       errors.getLast().getFirst());
2593          
2594          //gives an error if right side is not a number
2595          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2596                       _etc.forComparisonExpressionOnly(ce, SymbolData.DOUBLE_TYPE.getInstanceData(), 
2597                                                        _sd1.getInstanceData()));
2598          assertEquals("There should be three errors", 3, errors.size());
2599          assertEquals("Error message should be correct", 
2600                       "The right side of this expression is not a number.  Therefore, you cannot apply a Comparison "
2601                         + "Operator (<, >; <=, >=) to it", 
2602                       errors.getLast().getFirst());
2603          
2604          // Gives an error if right side is not an instance type
2605          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2606                       _etc.forComparisonExpressionOnly(ce, SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE));
2607          assertEquals("There should be four errors", 4, errors.size());
2608          assertEquals("Error message should be correct", "The right side of this expression is a type, not an instance.  "
2609                         + "Perhaps you meant to create a new instance of int", 
2610                       errors.getLast().getFirst());
2611        }
2612        
2613        
2614        public void testForShiftBinaryExpressionOnly() {
2615          ShiftBinaryExpression sbe = 
2616            new LeftShiftExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), 
2617                                    new IntegerLiteral(SourceInfo.NONE, 42));
2618          try {
2619            _etc.forShiftBinaryExpressionOnly(sbe, _sd2, _sd3);
2620            fail("forShiftBinaryExpressionOnly should have thrown a runtime exception");
2621          }
2622          catch (RuntimeException e) {
2623            assertEquals("Exception message should be correct", 
2624                         "Internal Program Error: BinaryShifts are not supported.  This should have been caught before "
2625                           + "the TypeChecker.  Please report this bug.", e.getMessage());
2626          }
2627        }
2628        
2629        
2630        public void testForPlusExpressionOnly() {
2631          PlusExpression pe = new PlusExpression(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
2632          SymbolData string = new SymbolData("java.lang.String");
2633          string.setPackage("java.lang");
2634          string.setIsContinuation(false);
2635          symbolTable.put("java.lang.String", string);
2636          
2637          //left side is a string instance data and right side is some other instance data
2638          assertEquals("Should return String instance", string.getInstanceData(), 
2639                       _etc.forPlusExpressionOnly(pe, string.getInstanceData(), _sd1.getInstanceData()));
2640          assertEquals("Should be no errors", 0, errors.size());
2641          
2642          //right side is a string instance data and left side is some other instance data
2643          assertEquals("Should return String instance", string.getInstanceData(), 
2644                       _etc.forPlusExpressionOnly(pe, _sd1.getInstanceData(), string.getInstanceData()));
2645          assertEquals("Should be no errors", 0, errors.size());
2646          
2647          //both left and right are string instance datas
2648          assertEquals("Should return String instance", string.getInstanceData(), 
2649                       _etc.forPlusExpressionOnly(pe, string.getInstanceData(), string.getInstanceData()));
2650          assertEquals("Should be no errors", 0, errors.size());
2651          
2652          //both left and right are numbers
2653          assertEquals("Should return Double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2654                       _etc.forPlusExpressionOnly(pe, SymbolData.DOUBLE_TYPE.getInstanceData(), 
2655                                                  SymbolData.INT_TYPE.getInstanceData()));
2656          assertEquals("Should be no errors", 0, errors.size());
2657          
2658          //one side is a string instance data, but the other is not an instance data
2659          assertEquals("Should return String instance", string.getInstanceData(), 
2660                       _etc.forPlusExpressionOnly(pe, string.getInstanceData(), _sd1));
2661          assertEquals("Should be one error", 1, errors.size());
2662          assertEquals("Error message should be correct", 
2663                       "The arguments to the Plus Operator (+) must both be instances, but you have specified a type "
2664                         + "name.  Perhaps you meant to create a new instance of " + _sd1.getName(), 
2665                       errors.getLast().getFirst());
2666          
2667          // One side is a string, not a string instance data
2668          assertEquals("Should return String instance", string.getInstanceData(), 
2669                       _etc.forPlusExpressionOnly(pe, string, string.getInstanceData()));
2670          assertEquals("Should be two errors", 2, errors.size());
2671          assertEquals("Error message should be correct", 
2672                       "The arguments to the Plus Operator (+) must both be instances, but you have specified a type "
2673                         + "name.  Perhaps you meant to create a new instance of java.lang.String", 
2674                       errors.getLast().getFirst());
2675          
2676          // One side is a number, the other is not
2677          assertEquals("Should return String instance", string.getInstanceData(), 
2678                       _etc.forPlusExpressionOnly(pe, SymbolData.INT_TYPE.getInstanceData(), 
2679                                                  SymbolData.BOOLEAN_TYPE.getInstanceData()));
2680          assertEquals("Should be three errors", 3, errors.size());
2681          assertEquals("Error message should be correct", 
2682                       "The arguments to the Plus Operator (+) must either include an instance of a String or both "
2683                         + "be numbers.  You have specified arguments of type int and boolean", 
2684                       errors.getLast().getFirst());
2685          
2686          //both sides are numbers, but the left side is not an instance data
2687          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2688                       _etc.forPlusExpressionOnly(pe, SymbolData.INT_TYPE, SymbolData.CHAR_TYPE.getInstanceData()));
2689          assertEquals("Should be 4 errors", 4, errors.size());
2690          assertEquals("Error message should be correct", 
2691                       "The arguments to the Plus Operator (+) must both be instances, but you have specified a type "
2692                         + "name.  Perhaps you meant to create a new instance of int", 
2693                       errors.getLast().getFirst());
2694    
2695          // Both sides are numbers, but the right side is not an instance data
2696          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2697                       _etc.forPlusExpressionOnly(pe, SymbolData.INT_TYPE.getInstanceData(), SymbolData.CHAR_TYPE));
2698          assertEquals("Should be 5 errors", 5, errors.size());
2699          assertEquals("Error message should be correct", 
2700                       "The arguments to the Plus Operator (+) must both be instances, but you have specified a type "
2701                         + "name.  Perhaps you meant to create a new instance of char", 
2702                       errors.getLast().getFirst());
2703          
2704          
2705        }
2706        
2707        
2708        public void testForNumericBinaryExpressionOnly() {
2709          NumericBinaryExpression nbe = new ModExpression(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
2710          
2711          //two number instance expressions work--returns least restrictive type
2712          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2713                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE.getInstanceData(), 
2714                                                           SymbolData.INT_TYPE.getInstanceData()));
2715          assertEquals("There should be no errors", 0, errors.size());
2716          
2717          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2718                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE.getInstanceData(), 
2719                                                           SymbolData.CHAR_TYPE.getInstanceData()));
2720          assertEquals("There should be no errors", 0, errors.size());
2721          
2722          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2723                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE.getInstanceData(), 
2724                                                           SymbolData.DOUBLE_TYPE.getInstanceData()));
2725          assertEquals("There should be no errors", 0, errors.size());
2726          
2727          //left not an instance data
2728          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2729                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE, 
2730                                                           SymbolData.DOUBLE_TYPE.getInstanceData()));
2731          assertEquals("Should be 1 error", 1, errors.size());
2732          assertEquals("Error message should be correct", "The left side of this expression is a type, not an instance.  "
2733                         + "Perhaps you meant to create a new instance of int", errors.getLast().getFirst());
2734          
2735          //left not a number
2736          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2737                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2738                                                           SymbolData.DOUBLE_TYPE.getInstanceData()));
2739          assertEquals("Should be 2 errors", 2, errors.size());
2740          assertEquals("Error message should be correct", "The left side of this expression is not a number.  "
2741                         + "Therefore, you cannot apply a Numeric Binary Operator (*, /, -, %) to it", 
2742                       errors.getLast().getFirst());
2743          
2744          // right not an instance data
2745          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2746                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE.getInstanceData(), 
2747                                                           SymbolData.DOUBLE_TYPE));
2748          assertEquals("Should be 3 errors", 3, errors.size());
2749          assertEquals("Error message should be correct", "The right side of this expression is a type, not an instance.  "
2750                         + "Perhaps you meant to create a new instance of double", errors.getLast().getFirst());
2751    
2752          // right not a number
2753          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2754                       _etc.forNumericBinaryExpressionOnly(nbe, SymbolData.INT_TYPE.getInstanceData(), 
2755                                                           SymbolData.BOOLEAN_TYPE.getInstanceData()));
2756          assertEquals("Should be 4 errors", 4, errors.size());
2757          assertEquals("Error message should be correct", 
2758                       "The right side of this expression is not a number.  Therefore, you cannot apply a "
2759                         + "Numeric Binary Operator (*, /, -, %) to it", errors.getLast().getFirst());
2760        }
2761        
2762        public void testForNoOpExpressionOnly() {
2763          NoOpExpression noe = new NoOpExpression(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
2764          try {
2765            _etc.forNoOpExpressionOnly(noe, null, null);
2766            fail("Should have thrown runtime exception");
2767          }
2768          catch (RuntimeException e) {
2769            assertEquals("Error message should be correct", "Internal Program Error: The student is missing an operator.  This should have been caught before the TypeChecker.  Please report this bug.", e.getMessage());
2770          }
2771        }
2772        
2773        public void testForIncrementExpressionOnly() {
2774          IncrementExpression ie = 
2775            new PositivePrefixIncrementExpression(SourceInfo.NONE, 
2776                                                  new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
2777          
2778          //if value result is a number instance, should work fine
2779          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2780                       _etc.forIncrementExpressionOnly(ie, SymbolData.INT_TYPE.getInstanceData()));
2781          assertEquals("Should be no errors", 0, errors.size());
2782          
2783          
2784          //if valueRes is null, return null but do not give error
2785          assertEquals("Should return null", null, _etc.forIncrementExpressionOnly(ie, null));
2786          assertEquals("Should be no errors", 0, errors.size());
2787          
2788          //if valueRes is PackageData, give error and return null
2789          PackageData pd = new PackageData("bad_reference");
2790          assertEquals("Should return null", null, _etc.forIncrementExpressionOnly(ie, pd));
2791          assertEquals("Should be 1 error", 1, errors.size());
2792          assertEquals("Error message should be correct", "Could not resolve symbol bad_reference", errors.getLast().getFirst());
2793          
2794          // if valueRes is not an instance type, give an error
2795          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2796                       _etc.forIncrementExpressionOnly(ie, SymbolData.INT_TYPE));
2797          assertEquals("Should be 2 errors", 2, errors.size());
2798          assertEquals("Error message should be correct", 
2799                       "You cannot increment or decrement int, because it is a class name not an instance.  "
2800                         + "Perhaps you meant to create a new instance of int", errors.getLast().getFirst());
2801          
2802          // if value result is not a number type, give an error
2803          assertEquals("Should return sd2 instance", _sd2.getInstanceData(), 
2804                       _etc.forIncrementExpressionOnly(ie, _sd2.getInstanceData()));
2805          assertEquals("Should be 3 errors", 3, errors.size());
2806          assertEquals("Error message should be correct", 
2807                       "You cannot increment or decrement something that is not a number type.  You have specified "
2808                         + "something of type " + _sd2.getName(), 
2809                       errors.getLast().getFirst());
2810        }
2811        
2812        
2813        
2814        public void testForNumericUnaryExpressionOnly() {
2815          NumericUnaryExpression nue = new PositiveExpression(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5));
2816          //number types like char and byte should be widened to int
2817          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2818                       _etc.forNumericUnaryExpressionOnly(nue, SymbolData.CHAR_TYPE.getInstanceData()));
2819          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2820                       _etc.forNumericUnaryExpressionOnly(nue, SymbolData.BYTE_TYPE.getInstanceData()));
2821          assertEquals("There should be no errors", 0, errors.size());
2822          
2823          //double type should be kept the same
2824          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2825                       _etc.forNumericUnaryExpressionOnly(nue, SymbolData.DOUBLE_TYPE.getInstanceData()));
2826          assertEquals("There should be no errors", 0, errors.size());
2827          
2828          //not an instance type
2829          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
2830                       _etc.forNumericUnaryExpressionOnly(nue, SymbolData.INT_TYPE));
2831          assertEquals("Should be one error", 1, errors.size());
2832          assertEquals("Error message should be correct", 
2833                       "You cannot use a numeric unary operator (+, -) with int, because it is a class name, "
2834                         + "not an instance.  Perhaps you meant to create a new instance of int", 
2835                       errors.getLast().getFirst());
2836          
2837          //not a number type
2838          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2839                       _etc.forNumericUnaryExpressionOnly(nue, SymbolData.BOOLEAN_TYPE.getInstanceData()));
2840          assertEquals("Should be 2 errors", 2, errors.size());
2841          assertEquals("Error message should be correct", 
2842                       "You cannot apply this unary operator to something of type boolean.  You can only apply it "
2843                         + "to a numeric type such as double, int, or char", errors.getLast().getFirst());
2844        }
2845    
2846        public void testForBitwiseNotExpressionOnly() {
2847          BitwiseNotExpression bne = 
2848            new BitwiseNotExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "t")));
2849          try {
2850            _etc.forBitwiseNotExpressionOnly(bne, _sd3);
2851            fail("forBitwiseNotExpressionOnly should have thrown a runtime exception");
2852          }
2853          catch (RuntimeException e) {
2854            assertEquals("Exception message should be correct", "Internal Program Error: BitwiseNot is not supported.  It should have been caught before getting to the TypeChecker.  Please report this bug.", e.getMessage());
2855          }
2856        }
2857        
2858        
2859        public void testForNotExpressionOnly() {
2860          NotExpression ne = new NotExpression(SourceInfo.NONE, NULL_LITERAL);
2861          
2862          //should work with a boolean instance
2863          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2864                       _etc.forNotExpressionOnly(ne, SymbolData.BOOLEAN_TYPE.getInstanceData()));
2865          assertEquals("Should be no errors", 0, errors.size());
2866          
2867          //not an instance type
2868          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2869                       _etc.forNotExpressionOnly(ne, SymbolData.BOOLEAN_TYPE));
2870          assertEquals("Should be one error", 1, errors.size());
2871          assertEquals("Error message should be correct",
2872                       "You cannot use the not (!) operator with boolean, because it is a class name, not an instance.  "
2873                         + "Perhaps you meant to create a new instance of boolean", errors.getLast().getFirst());
2874          
2875          //not a boolean type
2876          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
2877                       _etc.forNotExpressionOnly(ne, SymbolData.INT_TYPE.getInstanceData()));
2878          assertEquals("Should be two errors", 2, errors.size());
2879          assertEquals("Error message should be correct", 
2880                       "You cannot use the not (!) operator with something of type int. Instead, it should be used "
2881                         + "with an expression of boolean type", errors.getLast().getFirst());
2882        }
2883        
2884        
2885        public void testForConditionalExpressionOnly() {
2886          SymbolData sd1 = SymbolData.DOUBLE_TYPE;
2887          SymbolData sd2 = SymbolData.BOOLEAN_TYPE;
2888          SymbolData sd3 = SymbolData.INT_TYPE;
2889          ConditionalExpression cd = new ConditionalExpression(SourceInfo.NONE, 
2890                                                               new BooleanLiteral(SourceInfo.NONE, true),
2891                                                               new IntegerLiteral(SourceInfo.NONE, 5),
2892                                                               new IntegerLiteral(SourceInfo.NONE, 79));
2893          
2894          try {
2895            _etc.forConditionalExpressionOnly(cd, _sd3, _sd2, _sd1);
2896            fail("Should have thrown an exception.");
2897          }
2898          catch (Exception e) {
2899            assertEquals("Exception message should be correct", 
2900                         "Internal Program Error: Conditional expressions are not supported.  This should have been "
2901                           + "caught before the TypeChecker.  Please report this bug.", e.getMessage());
2902            
2903          }
2904        }  
2905        
2906        public void testForInstanceOfExpressionOnly() {
2907          SymbolData sd1 = SymbolData.DOUBLE_TYPE;
2908          SymbolData sd2 = SymbolData.BOOLEAN_TYPE;
2909          SymbolData sd3 = SymbolData.INT_TYPE;
2910          InstanceofExpression ioe = new InstanceofExpression(SourceInfo.NONE, NULL_LITERAL, JExprParser.NO_TYPE);
2911          assertEquals("When valueRes is subtype of typeRes, return BOOLEAN typeRes.", sd2.getInstanceData(), 
2912                       _etc.forInstanceofExpressionOnly(ioe, sd1, sd3.getInstanceData()));
2913          assertEquals("Should not throw an error.", 0, errors.size());
2914          assertEquals("When typeRes is subtype of valueRes, return BOOLEAN typeRes.", sd2.getInstanceData(), 
2915                       _etc.forInstanceofExpressionOnly(ioe, sd3, sd1.getInstanceData()));
2916          assertEquals("Should not throw an error.", 0, errors.size());
2917          assertEquals("When typeRes and valueRes are not subtypes of each other, return BOOLEAN typeRes", 
2918                       sd2.getInstanceData(),
2919                       _etc.forInstanceofExpressionOnly(ioe, sd2, sd1.getInstanceData()));
2920          assertEquals("Should now be one error.", 1, errors.size());
2921          assertEquals("Error message should be correct.", "You cannot test whether an expression of type " + sd1.getName() 
2922                         + " belongs to type " + sd2.getName() + " because they are not related", 
2923                       errors.getLast().getFirst());     
2924          SymbolData foo = new SymbolData("Foo");
2925          SymbolData fooMama = new SymbolData("FooMama");
2926          foo.setSuperClass(fooMama);
2927          assertEquals("When valueRes is a SymbolData, return BOOLEAN typeRes",  sd2.getInstanceData(), 
2928                       _etc.forInstanceofExpressionOnly(ioe, foo, fooMama));
2929          assertEquals("There should be 2 errors.", 2, errors.size());
2930          assertEquals("Error message should be correct.", 
2931                       "You are trying to test if FooMama belongs to type, but it is a class or interface type, "
2932                         + "not an instance.  Perhaps you meant to create a new instance of FooMama",
2933                       errors.getLast().getFirst());
2934        }
2935        
2936        public void testClassInstantiationHelper() {
2937          ClassInstantiation simpleCI = 
2938            new SimpleNamedClassInstantiation(SourceInfo.NONE, 
2939                                              new ClassOrInterfaceType(SourceInfo.NONE, "testClass", new Type[0]),
2940                                              new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
2941          ClassInstantiation complexCI = 
2942            new ComplexNamedClassInstantiation(SourceInfo.NONE,
2943                                               new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Outer")),
2944                                               new ClassOrInterfaceType(SourceInfo.NONE, "Inner", new Type[0]),
2945                                               new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
2946          
2947          ParenthesizedExpressionList pel = 
2948            new ParenthesizedExpressionList(SourceInfo.NONE, 
2949                                            new Expression[] {new SimpleNameReference(SourceInfo.NONE, 
2950                                                                                      new Word(SourceInfo.NONE, "int"))});
2951          ClassInstantiation badArgs =  
2952            new SimpleNamedClassInstantiation(SourceInfo.NONE,
2953                                              new ClassOrInterfaceType(SourceInfo.NONE, "anotherClass", new Type[0]),
2954                                              pel);
2955          
2956          SymbolData testClass = new SymbolData("testClass");
2957          SymbolData outer = new SymbolData("Outer");
2958          SymbolData outerInner = new SymbolData("Outer.Inner");
2959          outer.addInnerClass(outerInner);
2960          outerInner.setOuterData(outer);
2961          
2962          //classToInstantiate==null
2963          assertEquals("Should return null", null, _etc.classInstantiationHelper(simpleCI, null));
2964          assertEquals("Should be no errors", 0, errors.size());
2965          
2966          
2967          //if arg is a type instead of an instance, throw an error
2968          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), 
2969                       _etc.classInstantiationHelper(badArgs, SymbolData.DOUBLE_TYPE));
2970          assertEquals("Should be one error", 1, errors.size());
2971          assertEquals("Error message should be correct", 
2972                       "Cannot pass a class or interface name as a constructor argument.  Perhaps you meant to create a "
2973                         + "new instance of int", errors.getLast().getFirst());
2974          
2975          //if no matching constructor, give error
2976          assertEquals("Should return instance of testClass", testClass.getInstanceData(), 
2977                       _etc.classInstantiationHelper(simpleCI, testClass));
2978          assertEquals("Should be two errors", 2, errors.size());
2979          assertEquals("Error message should be correct", 
2980                       "No constructor found in class testClass with signature: testClass().", errors.getLast().getFirst());
2981          
2982          assertEquals("Should return instance of Outer.Inner", outerInner.getInstanceData(), 
2983                       _etc.classInstantiationHelper(complexCI, outerInner));
2984          assertEquals("Should be three errors", 3, errors.size());
2985          assertEquals("Error message should be correct", 
2986                       "No constructor found in class Outer.Inner with signature: Inner().", errors.getLast().getFirst());
2987          
2988          
2989          // if everything is in order, just return
2990          MethodData md = new MethodData("testClass", _publicMav, new TypeParameter[0], testClass, 
2991                                         new VariableData[0], 
2992                                         new String[0], 
2993                                         testClass,
2994                                         null);
2995          testClass.addMethod(md);
2996          assertEquals("Should return instance of testClass", testClass.getInstanceData(), 
2997                       _etc.classInstantiationHelper(simpleCI, testClass));
2998          assertEquals("Should still be just three errors", 3, errors.size());
2999        }
3000        
3001        
3002        public void testForSimpleNamedClassInstantiation() { 
3003          ParenthesizedExpressionList pel1 = 
3004            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new IntegerLiteral(SourceInfo.NONE, 5)});
3005          SimpleNamedClassInstantiation ci1 = 
3006            new SimpleNamedClassInstantiation(SourceInfo.NONE, 
3007                                              new ClassOrInterfaceType(SourceInfo.NONE, "simpleClass", new Type[0]), 
3008                                              pel1); 
3009          SimpleNamedClassInstantiation ci3 = 
3010            new SimpleNamedClassInstantiation(SourceInfo.NONE, 
3011                                              new ClassOrInterfaceType(SourceInfo.NONE, "simpleClass", new Type[0]), 
3012                                              new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
3013          
3014          // if the type is not in the symbolTable, an error should be added on lookup, and null should be returned:
3015          assertEquals("Should return null, since simpleClass is not in symbol table", null, ci1.visit(_etc));
3016          assertEquals("Should be 1 error", 1, errors.size());
3017          assertEquals("Error message should be correct", "Class or variable simpleClass not found.", errors.getLast().getFirst());
3018          
3019          //if class is in symbol table, but not visible from this context, should give an error but still return instance of type
3020          SymbolData simpleClass = new SymbolData("simpleClass");
3021          simpleClass.setIsContinuation(false);
3022          MethodData cons1 = new MethodData("simpleClass", _publicMav, new TypeParameter[0], simpleClass, 
3023                                            new VariableData[0], 
3024                                            new String[0], 
3025                                            simpleClass,
3026                                            null);
3027          simpleClass.addMethod(cons1);
3028          symbolTable.put("simpleClass", simpleClass);
3029          
3030          assertEquals("Should return simpleClass even though it could not really access it", 
3031                       simpleClass.getInstanceData(), ci3.visit(_etc));
3032          assertEquals("Should be 2 errors", 2, errors.size());
3033          assertEquals("Error message should be correct", 
3034                       "The class or interface simpleClass is package protected because there is no access specifier and "
3035                         + "cannot be accessed from i.like.monkey", 
3036                       errors.getLast().getFirst());
3037          
3038          // if class is in symbol table and visible, but there is not a matching constructor, should give an error 
3039          // but still return instance of type
3040          simpleClass.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3041          
3042          assertEquals("Should return simpleClass even though it could not find constructor", simpleClass.getInstanceData(), 
3043                       ci1.visit(_etc));
3044          assertEquals("Should be 3 errors", 3, errors.size());
3045          assertEquals("Error message should be correct", 
3046                       "No constructor found in class simpleClass with signature: simpleClass(int).", 
3047                       errors.getLast().getFirst());
3048          
3049          //if class is in symbol table, and there is a matching constructor, should not give any errors
3050          MethodData cons2 = new MethodData("simpleClass", _publicMav, new TypeParameter[0], simpleClass, 
3051                                            new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, 
3052                                            new String[0], 
3053                                            simpleClass,
3054                                            null);
3055          simpleClass.addMethod(cons2);                                   
3056          assertEquals("Should return simpleClass", simpleClass.getInstanceData(), ci1.visit(_etc));
3057          assertEquals("Should still be 3 errors", 3, errors.size());
3058          
3059          
3060          //if class is abstract, cannot be instantiated
3061          simpleClass.addModifier("abstract");
3062          assertEquals("Should return simpleClass even though it cannot really be instantiated", 
3063                       simpleClass.getInstanceData(), ci1.visit(_etc));
3064          assertEquals("Should be 4 errors", 4, errors.size());
3065          assertEquals("Error message should be correct", "simpleClass is abstract and thus cannot be instantiated", 
3066                       errors.getLast().getFirst());
3067          
3068          
3069          //now, what if we are dealing with an inner class?
3070          SimpleNamedClassInstantiation ci2 = 
3071            new SimpleNamedClassInstantiation(SourceInfo.NONE, 
3072                                              new ClassOrInterfaceType(SourceInfo.NONE, "A.B", new Type[0]), 
3073                                              new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
3074          
3075          
3076          SymbolData a = new SymbolData("A");
3077          a.setIsContinuation(false);
3078          SymbolData b = new SymbolData("A$B");
3079          b.setIsContinuation(false);
3080          b.setOuterData(a);
3081          a.addInnerClass(b);
3082          MethodData consb = new MethodData("B", _publicMav, new TypeParameter[0], b, 
3083                                            new VariableData[0], 
3084                                            new String[0], 
3085                                            b,
3086                                            null);
3087          b.addMethod(consb);
3088          symbolTable.put("A", a);
3089          a.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3090          b.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3091    
3092          //if inner part is not static, give error
3093          assertEquals("Should return A.B", b.getInstanceData(), ci2.visit(_etc));
3094          assertEquals("Should be 5 errors", 5, errors.size());
3095          assertEquals("Error message should be correct", 
3096                       "A.B is not a static inner class, and thus cannot be instantiated from this context.  Perhaps "
3097                         +"you meant to use an instantiation of the form new A().new B()", 
3098                       errors.getLast().getFirst());
3099          //if inner part is static, no problem
3100          b.addModifier("static");
3101          assertEquals("Should return A.B", b.getInstanceData(), ci2.visit(_etc));
3102          assertEquals("Should still be just 5 errors", 5, errors.size()); 
3103        }
3104        
3105        public void testForComplexNamedClassInstantiation() {
3106          ParenthesizedExpressionList pel1 = 
3107            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] { new IntegerLiteral(SourceInfo.NONE, 5)});
3108          ComplexNamedClassInstantiation ci1 = 
3109            new ComplexNamedClassInstantiation(SourceInfo.NONE, 
3110                                               new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "o")), 
3111                                               new ClassOrInterfaceType(SourceInfo.NONE, "innerClass", new Type[0]),                                  
3112                                               pel1);
3113          
3114          ComplexNamedClassInstantiation ci2 = 
3115            new ComplexNamedClassInstantiation(SourceInfo.NONE, 
3116                                               new SimpleNameReference(SourceInfo.NONE, 
3117                                                                       new Word(SourceInfo.NONE, "o")), 
3118                                               new ClassOrInterfaceType(SourceInfo.NONE, "innerClass", new Type[0]), 
3119                                               new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
3120          
3121          //if outer type is not in vars list, give appropriate error
3122          assertEquals("Should return null", null, ci1.visit(_etc));
3123          assertEquals("Should be 1 error", 1, errors.size());
3124          assertEquals("Error message should be correct", "Could not resolve symbol o", errors.getLast().getFirst());
3125          
3126          // if outer class is in symbol table and visible, but there is not a matching inner constructor, should give 
3127          // an error but still return instance of type
3128          SymbolData outerClass = new SymbolData("outer");
3129          outerClass.setIsContinuation(false);
3130          SymbolData innerClass = new SymbolData("outer$innerClass");
3131          innerClass.setIsContinuation(false);
3132          outerClass.addInnerClass(innerClass);
3133          innerClass.setOuterData(outerClass);
3134          MethodData cons1 = new MethodData("innerClass", _publicMav, new TypeParameter[0], innerClass, 
3135                                            new VariableData[0], 
3136                                            new String[0], 
3137                                            innerClass,
3138                                            null);
3139          innerClass.addMethod(cons1);
3140          symbolTable.put("outer", outerClass);
3141          _etc._vars.addLast(new VariableData("o", _publicMav, outerClass, true, _etc._data));
3142          outerClass.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3143          innerClass.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3144          
3145          assertEquals("Should return innerClass even though it could not find constructor", innerClass.getInstanceData(), 
3146                       ci1.visit(_etc));
3147          assertEquals("Should be 2 errors", 2, errors.size());
3148          assertEquals("Error message should be correct",
3149                       "No constructor found in class outer.innerClass with signature: innerClass(int).", 
3150                       errors.getLast().getFirst());
3151          
3152          //if class is in symbol table, and there is a matching constructor, should not give any errors
3153          MethodData cons2 = new MethodData("innerClass", _publicMav, new TypeParameter[0], innerClass, 
3154                                            new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, 
3155                                            new String[0], 
3156                                            innerClass,
3157                                            null);
3158          innerClass.addMethod(cons2);                                   
3159          assertEquals("Should return innerClass", innerClass.getInstanceData(), ci1.visit(_etc));
3160          assertEquals("Should still be 2 errors", 2, errors.size());
3161          
3162          //if class is abstract, cannot be instantiated
3163          innerClass.addModifier("abstract");
3164          assertEquals("Should return innerClass even though it cannot really be instantiated", innerClass.getInstanceData(), 
3165                       ci1.visit(_etc));
3166          assertEquals("Should be 3 errors", 3, errors.size());
3167          assertEquals("Error message should be correct", "outer.innerClass is abstract and thus cannot be instantiated", 
3168                       errors.getLast().getFirst());              
3169          
3170          //if enclosingType is not an instance, and result is not static, give error
3171          ComplexNamedClassInstantiation ci3 = 
3172            new ComplexNamedClassInstantiation(SourceInfo.NONE, 
3173                                               new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "outer")), 
3174                                               new ClassOrInterfaceType(SourceInfo.NONE, "innerClass", new Type[0]), 
3175                                               new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));             
3176          outerClass.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3177          innerClass.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
3178          assertEquals("Should return innerClass even though the syntax was wrong", innerClass.getInstanceData(), 
3179                       ci3.visit(_etc));
3180          assertEquals("Should be 4 errors", 4, errors.size());
3181          assertEquals("Error message should be correct", 
3182                       "The constructor of a non-static inner class can only be called on an instance of its containing "
3183                         + "class (e.g. new outer().new innerClass())", 
3184                       errors.getLast().getFirst());
3185          
3186          //if result is static, give appropriate error:
3187          innerClass.addModifier("static");
3188          assertEquals("Should return innerClass even though the syntax was wrong", 
3189                       innerClass.getInstanceData(), ci1.visit(_etc));
3190          assertEquals("Should be 5 errors", 5, errors.size());
3191          
3192          assertEquals("Error message should be correct", 
3193                       "You cannot instantiate a static inner class or interface with this syntax.  Instead, "
3194                         + "try new outer.innerClass()",
3195                       errors.getLast().getFirst());
3196          
3197          
3198          //if inner class of that name does not exist, give an error
3199          innerClass.setMav(_publicMav);
3200          ParenthesizedExpressionList pel2 = 
3201            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new IntegerLiteral(SourceInfo.NONE, 5)});
3202          ComplexNamedClassInstantiation ci4 = 
3203            new ComplexNamedClassInstantiation(SourceInfo.NONE, 
3204                                               new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "o")), 
3205                                               new ClassOrInterfaceType(SourceInfo.NONE, "notInnerClass", new Type[0]), 
3206                                               pel2);
3207          assertEquals("Should return null", null, ci4.visit(_etc));
3208          assertEquals("Should be 6 errors", 6, errors.size());
3209          assertEquals("Error message should be correct", "Class or variable notInnerClass not found.", 
3210                       errors.getLast().getFirst());
3211          
3212          
3213          //if inner class is private, give error
3214          innerClass.setMav(_privateMav);
3215          assertEquals("Should return inner class", innerClass.getInstanceData(), ci1.visit(_etc));
3216          assertEquals("Should be 7 errors", 7, errors.size());
3217          assertEquals("Error message should be correct", 
3218                       "The class or interface outer.innerClass in outer.innerClass is private and cannot be accessed from i.like.monkey", 
3219                       errors.getLast().getFirst());
3220          
3221          //if outer class is private, give error
3222          outerClass.setMav(_privateMav);
3223          innerClass.setMav(_publicMav);
3224          assertEquals("Should return inner class", innerClass.getInstanceData(), ci1.visit(_etc));
3225          assertEquals("Should be 8 errors", 8, errors.size());
3226          assertEquals("Error message should be correct", 
3227                       "The class or interface outer in outer is private and cannot be accessed from i.like.monkey", 
3228                       errors.getLast().getFirst());
3229        }
3230        
3231        public void testForSimpleThisConstructorInvocation() {
3232          //this should always add an error:
3233          SimpleThisConstructorInvocation stci = 
3234            new SimpleThisConstructorInvocation(SourceInfo.NONE, 
3235                                                new ParenthesizedExpressionList(SourceInfo.NONE, 
3236                                                                                new Expression[0]));
3237          assertEquals("Should return null", null, stci.visit(_etc));
3238          assertEquals("Should be 1 error", 1, errors.size());
3239          assertEquals("Error message should be correct", 
3240                       "This constructor invocations are only allowed as the first statement of a constructor body", 
3241                       errors.getLast().getFirst());
3242        }
3243        
3244        public void testForComplexThisConstructorInvocation() {
3245          //this should always add an error
3246          ComplexThisConstructorInvocation ctci = 
3247            new ComplexThisConstructorInvocation(SourceInfo.NONE, 
3248                                                 new SimpleNameReference(SourceInfo.NONE,
3249                                                                         new Word(SourceInfo.NONE, "something")), 
3250                                                 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
3251          assertEquals("Should return null", null, ctci.visit(_etc));
3252          assertEquals("Should be 1 error", 1, errors.size());
3253          assertEquals("Error message should be correct", 
3254                       "Constructor invocations of this form are never allowed", 
3255                       errors.getLast().getFirst());
3256        }
3257        
3258        public void testForSimpleNameReference() {
3259          //first, consider the case where what we have is a variable reference:
3260          SimpleNameReference var = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "variable1"));
3261          VariableData varData = new VariableData("variable1", _publicMav, SymbolData.INT_TYPE, false, _etc._data);
3262          _etc._vars.add(varData);
3263          
3264          //in this case, it has not been initialized--should throw error
3265          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_etc));
3266          assertEquals("Should be 1 error", 1, errors.size());
3267          assertEquals("Error message should be correct", 
3268                       "You cannot use variable1 because it may not have been given a value", 
3269                       errors.getLast().getFirst());
3270          
3271          // if it has been initialized, do not give an error
3272          varData.gotValue();
3273          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_etc));
3274          assertEquals("Should still be 1 error", 1, errors.size());
3275          
3276          // if variable is non-static, but you are in static context, cannot reference it. Should give error
3277          MethodData newContext =
3278            new MethodData("method", _publicStaticMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[0], 
3279                           new String[0], _sd1, NULL_LITERAL); 
3280          _etc._data = newContext;
3281          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_etc));
3282          assertEquals("Should be 2 errors", 2, errors.size());
3283          assertEquals("Error message should be correct", 
3284                       "Non-static variable or field variable1 cannot be referenced from a static context", 
3285                       errors.getLast().getFirst());
3286          
3287          // Test reference to private local variable in a method
3288          SimpleNameReference var2 = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "variable1"));
3289          MethodData newContext2 = 
3290            new MethodData("method2", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[0], 
3291                           new String[0], _sd1, NULL_LITERAL);
3292          VariableData varData2 = new VariableData("variable2", _privateMav, SymbolData.INT_TYPE, false, newContext2);
3293          newContext2.addVar(varData2);
3294          
3295          varData2.gotValue();  // Give the private variable a value
3296          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var2.visit(_etc));
3297          assertEquals("Should be still be 2 errors", 2, errors.size());
3298          
3299          _etc._data = _sd1;
3300            
3301          // if it is a variable of your super class, it won't be in _vars.  Check this case.
3302          _etc._vars = new LinkedList<VariableData>();
3303          _sd1.setSuperClass(_sd2);
3304          _sd2.addVar(varData);
3305          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_etc));
3306          assertEquals("Should still be 2 errors", 2, errors.size());
3307          
3308          // now, consider the case where what we have is a class reference:
3309          SimpleNameReference className = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Frog"));
3310          SymbolData frog = new SymbolData("Frog");
3311          frog.setIsContinuation(false);
3312          symbolTable.put("Frog", frog);
3313          
3314          //if it is not visibile from this context, return package data
3315          TypeData result = className.visit(_etc);
3316          assertTrue("Result should be a PackageData since Frog is not accessible", result instanceof PackageData);
3317          assertEquals("Should have correct name", "Frog", result.getName());
3318          assertEquals("Should still be 2 errors", 2, errors.size());
3319          
3320          // if it is visibile from this context, no error
3321          frog.setMav(_publicMav);
3322          assertEquals("Should return Frog", frog, className.visit(_etc));
3323          assertEquals("Should still be 2 errors", 2, errors.size());
3324          
3325          // Finally, if the name cannot be resolved, simply return a packageData.
3326          SimpleNameReference fake = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "notRealReference"));
3327          assertEquals("Should return package data", "notRealReference", (fake.visit(_etc)).getName());
3328          assertEquals("Should still be just 2 errors", 2, errors.size());
3329          
3330          // if the reference is ambiguous (matches both an interface and a class) give an error
3331          SimpleNameReference ambigRef = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "ambigThing"));
3332          
3333          SymbolData interfaceD = new SymbolData("interface");
3334          interfaceD.setIsContinuation(false);
3335          interfaceD.setInterface(true);
3336          interfaceD.setMav(_publicMav);
3337          
3338          SymbolData classD = new SymbolData("superClass");
3339          classD.setIsContinuation(false);
3340          classD.setMav(_publicMav);
3341          
3342          SymbolData ambigThingI = new SymbolData("ambigThing");
3343          ambigThingI.setIsContinuation(false);
3344          ambigThingI.setInterface(true);
3345          interfaceD.addInnerInterface(ambigThingI);
3346          ambigThingI.setOuterData(interfaceD);
3347          ambigThingI.setMav(_publicStaticMav);
3348          
3349          SymbolData ambigThingC = new SymbolData("ambigThing");
3350          ambigThingC.setIsContinuation(false);
3351          classD.addInnerClass(ambigThingC);
3352          ambigThingC.setOuterData(classD);
3353          ambigThingC.setMav(_publicStaticMav);
3354          
3355          _sd6.addInterface(interfaceD);
3356          _sd6.setSuperClass(classD);
3357          
3358          _sd6.setMav(_publicMav);
3359          _sd6.setIsContinuation(false);
3360          
3361          _etc._data = _sd6;
3362          
3363          assertEquals("Should return null", null, ambigRef.visit(_etc));
3364          assertEquals("Should be 3 errors", 3, errors.size());
3365          assertEquals("Error message should be correct", 
3366                       "Ambiguous reference to class or interface ambigThing", 
3367                       errors.getLast().getFirst());    
3368        }
3369        
3370        
3371        public void testForComplexNameReference() {
3372          //if lhs is a package data, we want to keep building it:
3373          
3374          //if whole reference is just package reference, return package data
3375          ComplexNameReference ref1 = 
3376            new ComplexNameReference(SourceInfo.NONE, 
3377                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "java")), 
3378                                     new Word(SourceInfo.NONE, "lang"));
3379          assertEquals("Should return correct package data", "java.lang", ref1.visit(_etc).getName());
3380          assertEquals("Should be no errors", 0, errors.size());
3381          
3382          // if reference builds to a class in the symbol table, return that class
3383          ComplexNameReference ref2 = 
3384            new ComplexNameReference(SourceInfo.NONE, ref1, new Word(SourceInfo.NONE, "String"));
3385          assertTrue("symbol table already contains String", symbolTable.containsKey("java.lang.String"));
3386          SymbolData string = symbolTable.get("java.lang.String");          
3387    //      SymbolData string = new SymbolData("java.lang.String");
3388    //      string.setPackage("java.lang");
3389    //      string.setMav(_publicMav);
3390    //      string.setIsContinuation(false);
3391    //      symbolTable.put("java.lang.String", string);
3392          
3393          assertEquals("Should return string", string, ref2.visit(_etc));
3394          
3395          assertEquals("Should still be no errors", 0, errors.size());
3396          
3397          
3398          //if lhs is not a package data, it gets more complicated:
3399          
3400          // we're referencing a variable inside of symbol data lhs:
3401          VariableData myVar = new VariableData("myVar", _publicStaticMav, SymbolData.DOUBLE_TYPE, true, string);
3402          string.addVar(myVar);
3403          ComplexNameReference varRef1 = new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "myVar"));
3404          
3405          // static var from static context
3406          assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_etc));
3407          assertEquals("There should still be no errors", 0, errors.size());
3408          
3409          // Static uninitialized var from static context
3410          myVar.lostValue();
3411          assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_etc));
3412          assertEquals("There should still be one error", 1, errors.size());
3413          assertEquals("Error message should be correct", 
3414                       "You cannot use myVar here, because it may not have been given a value", 
3415                       errors.getLast().getFirst());
3416          
3417          // Non-static var--this is a static context
3418          myVar.gotValue();
3419          myVar.setMav(_publicMav);
3420          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_etc));
3421          assertEquals("Should be 2 errors", 2, errors.size());
3422          assertEquals("Error message should be correct", 
3423                       "Non-static variable myVar cannot be accessed from the static context java.lang.String.  "
3424                         + "Perhaps you meant to instantiate an instance of java.lang.String", 
3425                       errors.getLast().getFirst());
3426          
3427          // Non-static context, okay to reference non-static var
3428          VariableData stringVar = new VariableData("s", _publicMav, string, true, _etc._data);  
3429          _etc._vars.add(stringVar);
3430          ComplexNameReference varRef2 = 
3431            new ComplexNameReference(SourceInfo.NONE, 
3432                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s")), 
3433                                     new Word(SourceInfo.NONE, "myVar"));
3434          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_etc));
3435          assertEquals("Should still just be 2 errors", 2, errors.size());
3436          
3437          // Non-static context, okay to reference private non-static var
3438          VariableData privateStringVar = new VariableData("ps", _privateMav, string, true, _etc._data);
3439          _etc._vars.add(privateStringVar);
3440          ComplexNameReference varRef25 = 
3441            new ComplexNameReference(SourceInfo.NONE, 
3442                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "ps")), 
3443                                     new Word(SourceInfo.NONE, "myVar"));
3444          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef25.visit(_etc));
3445          assertEquals("Should still just be 2 errors", 2, errors.size());
3446          
3447          // if it is a variable of the super class, you should still be able to see it.  Check this case.
3448          string.setVars(new LinkedList<VariableData>());
3449          string.setSuperClass(_sd2);
3450          _sd2.addVar(myVar);
3451          assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_etc));
3452          assertEquals("Should still be 2 errors", 2, errors.size());
3453          
3454          // a complex multiple variable reference case:
3455          VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, true, _sd1);   // was _publicMav
3456          VariableData vd2 = new VariableData("Santa's Little Helper", _publicMav, _sd1, true, _sd2); // was _publicMav
3457          VariableData vd3 = new VariableData("Snowball1", _publicMav, _sd2, true, _sd3);             // was _publicMav
3458          _sd3.addVar(vd3);
3459          _sd2.addVar(vd2);
3460          _sd1.addVar(vd1);
3461          
3462          ComplexNameReference varRef3 = 
3463            new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, 
3464                                                                                                        "Snowball1")),
3465                                     new Word(SourceInfo.NONE, "Santa's Little Helper"));
3466          ComplexNameReference varRef4 = 
3467            new ComplexNameReference(SourceInfo.NONE, varRef3, new Word(SourceInfo.NONE, "Mojo"));
3468          
3469          Data oldData = _etc._data;
3470          _etc._data = _sd3;
3471          _etc._vars.add(vd3);
3472          _sd3.setMav(_publicMav);
3473          _sd1.setMav(_publicMav);
3474          _sd2.setMav(_publicMav);
3475          
3476          TypeData result = varRef4.visit(_etc);
3477          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), result);
3478          assertEquals("Should still be 2 errors", 2, errors.size());
3479          
3480          _etc._data = oldData;
3481    
3482          // What if what we have is an inner class?
3483          SymbolData inner = new SymbolData("java.lang.String$Inner");
3484          inner.setPackage("java.lang");
3485          inner.setIsContinuation(false);
3486          inner.setOuterData(string);
3487          string.addInnerClass(inner);
3488          
3489          //if inner is not visible, throw error
3490          ComplexNameReference innerRef0 = 
3491            new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, 
3492                                                                              new Word(SourceInfo.NONE, "s")),
3493                                     new Word(SourceInfo.NONE, "Inner"));
3494          assertEquals("Should return null", null, innerRef0.visit(_etc));
3495          assertEquals("Should be 3 errors", 3, errors.size());
3496          assertEquals("Error message should be correct", 
3497                       "The class or interface java.lang.String.Inner is package protected because there is no access "
3498                         + "specifier and cannot be accessed from i.like.monkey", 
3499                       errors.getLast().getFirst());
3500          
3501          inner.setMav(_publicMav);
3502          
3503          
3504          //if inner is not static, give error:
3505          ComplexNameReference innerRef1 = 
3506            new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "Inner"));
3507          assertEquals("Should return inner", inner, innerRef1.visit(_etc));
3508          assertEquals("Should be 4 errors", 4, errors.size());
3509          assertEquals("Error message should be correct", 
3510                       "Non-static inner class java.lang.String.Inner cannot be accessed from this context.  "
3511                         + "Perhaps you meant to instantiate it", errors.getLast().getFirst());
3512          
3513          //if inner is not static and outer is not static, it's okay...
3514          ComplexNameReference innerRef2 = 
3515            new ComplexNameReference(SourceInfo.NONE, 
3516                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s")), 
3517                                     new Word(SourceInfo.NONE, "Inner"));
3518          assertEquals("Should return inner", inner, innerRef2.visit(_etc));
3519          assertEquals("Should still be 5 errors", 5, errors.size());
3520          assertEquals("Error message should be correct", 
3521                       "Non-static inner class java.lang.String.Inner cannot be accessed from this context.  "
3522                         + "Perhaps you meant to instantiate it", 
3523                       errors.getLast().getFirst());
3524          
3525          //if inner is static and outer is not static, throw error
3526          inner.setMav(_publicStaticMav);
3527          assertEquals("Should return inner", inner, innerRef2.visit(_etc));
3528          assertEquals("Should be 6 errors", 6, errors.size());
3529          assertEquals("Error message should be correct", 
3530                       "You cannot reference the static inner class java.lang.String.Inner from an instance of "
3531                         + "java.lang.String.  Perhaps you meant to say java.lang.String.Inner", 
3532                       errors.getLast().getFirst());
3533          
3534          
3535          //if the symbol could not be matched, give an error and return null
3536          ComplexNameReference noSense = 
3537            new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "nonsense"));
3538          assertEquals("Should return null", null, noSense.visit(_etc));
3539          assertEquals("Should be 7 errors", 7, errors.size());
3540          assertEquals("Error message should be correct", "Could not resolve nonsense from the context of java.lang.String", 
3541                       errors.getLast().getFirst());
3542          
3543          //if the reference is ambiguous (matches both an interface and a class) give an error
3544          ComplexNameReference ambigRef = 
3545            new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, 
3546                                                                              new Word(SourceInfo.NONE, "cebu")), 
3547                                     new Word(SourceInfo.NONE, "ambigThing"));
3548          
3549          SymbolData interfaceD = new SymbolData("interface");
3550          interfaceD.setIsContinuation(false);
3551          interfaceD.setInterface(true);
3552          interfaceD.setMav(_publicMav);
3553          
3554          SymbolData classD = new SymbolData("superClass");
3555          classD.setIsContinuation(false);
3556          classD.setMav(_publicMav);
3557          
3558          SymbolData ambigThingI = new SymbolData("ambigThing");
3559          ambigThingI.setIsContinuation(false);
3560          ambigThingI.setInterface(true);
3561          interfaceD.addInnerInterface(ambigThingI);
3562          ambigThingI.setOuterData(interfaceD);
3563          ambigThingI.setMav(_publicStaticMav);
3564          
3565          SymbolData ambigThingC = new SymbolData("ambigThing");
3566          ambigThingC.setIsContinuation(false);
3567          classD.addInnerClass(ambigThingC);
3568          ambigThingC.setOuterData(classD);
3569          ambigThingC.setMav(_publicStaticMav);
3570          
3571          _sd6.addInterface(interfaceD);
3572          _sd6.setSuperClass(classD);
3573          
3574          symbolTable.put("cebu", _sd6);
3575          _sd6.setMav(_publicMav);
3576          _sd6.setIsContinuation(false);
3577          
3578          assertEquals("Should return null", null, ambigRef.visit(_etc));
3579          assertEquals("Should be 8 errors", 8, errors.size());
3580          assertEquals("Error message should be correct", 
3581                       "Ambiguous reference to class or interface ambigThing", 
3582                       errors.getLast().getFirst());    
3583          
3584          //if lhs is not visible or inner is not visible, should throw error
3585          inner.setMav(_publicStaticMav);
3586          string.setMav(_privateMav);
3587          
3588          assertEquals("Should return inner", inner, innerRef1.visit(_etc));
3589          assertEquals("Should be 9 errors", 9, errors.size());
3590          assertEquals("Error message should be correct", 
3591                       "The class or interface java.lang.String in java.lang.String is private and cannot be accessed from i.like.monkey", 
3592                       errors.getLast().getFirst());
3593        }
3594        
3595        
3596        public void testForSimpleThisReference() {
3597          SimpleThisReference str = new SimpleThisReference(SourceInfo.NONE);
3598          
3599          //as long as we are not in a static method, this should be fine.
3600          assertEquals("Should return i.like.monkey instance", _etc._data.getSymbolData().getInstanceData(), str.visit(_etc));
3601          assertEquals("Should be no errors", 0, errors.size());
3602          
3603          //if we are in a static method, give appropriate error
3604          MethodData sm = new MethodData("staticMethod", new VariableData[0]);
3605          sm.setMav(_publicStaticMav);
3606          sm.setOuterData(_etc._data);
3607          _etc._data = sm;
3608          
3609          assertEquals("Should return i.like.monkey instance", _etc._data.getSymbolData().getInstanceData(), 
3610                       str.visit(_etc));
3611          assertEquals("Should be one errors", 1, errors.size());
3612          assertEquals("Error message should be correct", "'this' cannot be referenced from within a static method", 
3613                       errors.getLast().getFirst());
3614        }
3615        
3616        
3617        public void testForComplexThisReferenceOnly() {
3618          ComplexThisReference ctr = 
3619            new ComplexThisReference(SourceInfo.NONE, 
3620                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "context")));
3621          
3622          // if enclosing_result is null, return null
3623          assertEquals("Should return null", null, _etc.forComplexThisReferenceOnly(ctr, null));
3624          assertEquals("Should be no errors", 0, errors.size());
3625          
3626          // if enclosing result is a PackageData, give appropriate error and return null
3627          assertEquals("Should return null", null, _etc.forComplexThisReferenceOnly(ctr, new PackageData("context")));
3628          assertEquals("Should be 1 error", 1, errors.size());
3629          assertEquals("Error message should be correct","Could not resolve symbol context" , errors.getLast().getFirst());
3630          
3631          // if enclosing_result is not an outer data of the current context, give an error
3632          SymbolData contextClass = new SymbolData("context");
3633          contextClass.setIsContinuation(false);
3634          contextClass.setMav(_publicMav);
3635          assertEquals("Should return instance of this", contextClass.getInstanceData(), 
3636                       _etc.forComplexThisReferenceOnly(ctr, contextClass));
3637          assertEquals("Should be 2 errors", 2, errors.size());
3638          assertEquals("The error message should be correct", "You cannot reference context.this from here, "
3639                         + "because context is not an outer class of i.like.monkey", 
3640                       errors.getLast().getFirst());
3641          
3642          // if enclosing_result is an outer data of current context, everything is peachy
3643          _etc._data.setOuterData(contextClass);
3644          contextClass.addInnerClass(_etc._data.getSymbolData());
3645          assertEquals("Should return instance of this", contextClass.getInstanceData(), 
3646                       _etc.forComplexThisReferenceOnly(ctr, contextClass));
3647          assertEquals("Should still be 2 errors", 2, errors.size());
3648          
3649          // if we are in a static method, throw appropriate error
3650          MethodData sm = new MethodData("staticMethod", new VariableData[0]);
3651          sm.setMav(_publicStaticMav);
3652          sm.setOuterData(_etc._data);
3653          _etc._data = sm;
3654          assertEquals("Should return instance of this", contextClass.getInstanceData(), 
3655                       _etc.forComplexThisReferenceOnly(ctr, contextClass));
3656          assertEquals("Should be 3 errors", 3, errors.size());
3657          assertEquals("The error message should be correct", "'this' cannot be referenced from within a static method", 
3658                       errors.getLast().getFirst());
3659          
3660          // if the enclosing result is an instance type, throw an error
3661          _etc._data = sm.getOuterData();
3662          assertEquals("Should return instance of this", contextClass.getInstanceData(), 
3663                       _etc.forComplexThisReferenceOnly(ctr, contextClass.getInstanceData()));
3664          assertEquals("Should be 4 errors", 4, errors.size());
3665          assertEquals("The error message should be correct", 
3666                       "'this' can only be referenced from a type name, but you have specified an instance of that type.", 
3667                       errors.getLast().getFirst());
3668          
3669          //if current context is static, give an error
3670          _etc._data.getSymbolData().addModifier("static");
3671          assertEquals("Should return instance of this", contextClass.getInstanceData(), 
3672                       _etc.forComplexThisReferenceOnly(ctr, contextClass));
3673          assertEquals("Should be 5 errors", 5, errors.size());
3674          assertEquals("Error message should be correct", 
3675                       "You cannot reference context.this from here, because i.like.monkey or one of its enclosing "
3676                         + "classes is static.  Thus, an enclosing instance of context does not exist", 
3677                       errors.getLast().getFirst());
3678        }
3679        
3680        public void testForSimpleSuperReference() {
3681          SimpleSuperReference ssr = new SimpleSuperReference(SourceInfo.NONE);
3682          _sd1.setSuperClass(_sd2);
3683          
3684          //normally, should work
3685          assertEquals("Should return _sd2", _sd2.getInstanceData(), ssr.visit(_etc));
3686          assertEquals("Should be no errors", 0, errors.size());
3687          
3688          //if within static method, add error
3689          MethodData sm = new MethodData("staticMethod", new VariableData[0]);
3690          sm.setMav(_publicStaticMav);
3691          sm.setOuterData(_etc._data);
3692          _etc._data = sm;
3693          
3694          assertEquals("Should return _sd2", _sd2.getInstanceData(), ssr.visit(_etc));
3695          assertEquals("Should be 1 error", 1, errors.size());
3696          assertEquals("Error message should be correct", "'super' cannot be referenced from within a static method", 
3697                       errors.getLast().getFirst());
3698        }
3699        
3700        
3701        public void testForComplexSuperReference() {
3702          ComplexSuperReference csr = 
3703            new ComplexSuperReference(SourceInfo.NONE, 
3704                                      new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "context")));
3705          
3706          // if enclosing_result is null, return null
3707          assertEquals("Should return null", null, _etc.forComplexSuperReferenceOnly(csr, null));
3708          assertEquals("Should be no errors", 0, errors.size());
3709    
3710          // if enclosing result is a PackageData, give appropriate error and return null
3711          assertEquals("Should return null", null, _etc.forComplexSuperReferenceOnly(csr, new PackageData("context")));
3712          assertEquals("Should be 1 error", 1, errors.size());
3713          assertEquals("Error message should be correct","Could not resolve symbol context" , errors.getLast().getFirst());
3714          
3715          // if enclosing_result is not an outer data of the current context, give an error
3716          SymbolData contextClass = new SymbolData("context");
3717          contextClass.setIsContinuation(false);
3718          contextClass.setMav(_publicMav);
3719          contextClass.setSuperClass(_sd2);
3720          assertEquals("Should return instance of super", _sd2.getInstanceData(), 
3721                       _etc.forComplexSuperReferenceOnly(csr, contextClass));
3722          assertEquals("Should be 2 errors", 2, errors.size());
3723          assertEquals("The error message should be correct", 
3724                       "You cannot reference context.super from here, because context is not an outer class of i.like.monkey", 
3725                       errors.getLast().getFirst());
3726          
3727          
3728          // if enclosing_result is an outer data of current context, everything is peachy
3729          _etc._data.setOuterData(contextClass);
3730          contextClass.addInnerClass(_etc._data.getSymbolData());
3731          assertEquals("Should return instance of super", _sd2.getInstanceData(), 
3732                       _etc.forComplexSuperReferenceOnly(csr, contextClass));
3733          assertEquals("Should still be 2 errors", 2, errors.size());
3734          
3735          // if we are in a static method, throw appropriate error
3736          MethodData sm = new MethodData("staticMethod", new VariableData[0]);
3737          sm.setMav(_publicStaticMav);
3738          sm.setOuterData(_etc._data);
3739          _etc._data = sm;
3740          assertEquals("Should return instance of super", _sd2.getInstanceData(), 
3741                       _etc.forComplexSuperReferenceOnly(csr, contextClass));
3742          assertEquals("Should be 3 errors", 3, errors.size());
3743          assertEquals("The error message should be correct", "'super' cannot be referenced from within a static method", 
3744                       errors.getLast().getFirst());
3745          
3746          // if the enclosing result is an instance type, throw an error
3747          _etc._data = sm.getOuterData();
3748          assertEquals("Should return instance of super", _sd2.getInstanceData(), 
3749                       _etc.forComplexSuperReferenceOnly(csr, contextClass.getInstanceData()));
3750          assertEquals("Should be 4 errors", 4, errors.size());
3751          assertEquals("The error message should be correct", "'super' can only be referenced from a type name, "
3752                         + "but you have specified an instance of that type.", 
3753                       errors.getLast().getFirst());
3754          
3755          // if current context is static, give an error
3756          _etc._data.getSymbolData().addModifier("static");
3757          assertEquals("Should return instance of super", _sd2.getInstanceData(), 
3758                       _etc.forComplexSuperReferenceOnly(csr, contextClass));
3759          assertEquals("Should be 5 errors", 5, errors.size());
3760          assertEquals("Error message should be correct", 
3761                       "You cannot reference context.super from here, because i.like.monkey or one of its enclosing "
3762                         + "classes is static.  Thus, an enclosing instance of context does not exist", 
3763                       errors.getLast().getFirst());
3764        }
3765        
3766        public void testForArrayAccessOnly() {
3767          ArrayAccess aa = 
3768            new ArrayAccess(SourceInfo.NONE, NULL_LITERAL, NULL_LITERAL);
3769          
3770          Hashtable<SymbolData, LanguageLevelVisitor> testNewSDs = LanguageLevelConverter._newSDs;
3771          LanguageLevelVisitor testLLVisitor = 
3772            new LanguageLevelVisitor(_etc._file, 
3773                                     _etc._package,
3774                                     null, // enclosingClassName for top level traversal
3775                                     _etc._importedFiles, 
3776                                     _etc._importedPackages, 
3777                                     new HashSet<String>(), 
3778                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
3779                                     new LinkedList<Command>());
3780          
3781          //if lhs is an array, and index is an int instance, no errors
3782          ArrayData ad = new ArrayData(SymbolData.INT_TYPE, testLLVisitor, SourceInfo.NONE);             
3783          
3784          assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), 
3785                       _etc.forArrayAccessOnly(aa, ad.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
3786          assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), 
3787                       _etc.forArrayAccessOnly(aa, ad.getInstanceData(), SymbolData.CHAR_TYPE.getInstanceData()));
3788          assertEquals("Should be no errors", 0, errors.size());
3789          
3790          //if either input is null, return null
3791          assertEquals("Should return null", null, _etc.forArrayAccessOnly(aa, null, SymbolData.INT_TYPE));
3792          assertEquals("Should return null", null, _etc.forArrayAccessOnly(aa, SymbolData.INT_TYPE, null));
3793          assertEquals("Should be no errors", 0, errors.size());
3794          
3795          //if lhs is a PackageData, give error and return null
3796          PackageData pd = new PackageData("bad_reference");
3797          assertEquals("Should return null", null, _etc.forArrayAccessOnly(aa, pd, SymbolData.INT_TYPE));
3798          assertEquals("Should be 1 error", 1, errors.size());
3799          assertEquals("Error message should be correct", 
3800                       "Could not resolve symbol bad_reference", 
3801                       errors.getLast().getFirst());
3802          
3803          
3804          //if rhs is a PackageData, give an error and return null
3805          assertEquals("Should return null", null, _etc.forArrayAccessOnly(aa, SymbolData.INT_TYPE, pd));
3806          assertEquals("Should still be 1 error", 1, errors.size());  // Generated a duplicate error
3807          assertEquals("Error message should be correct", 
3808                       "Could not resolve symbol bad_reference", 
3809                       errors.getLast().getFirst());
3810          
3811          //if array type is not an instance data, give appropriate error:
3812          assertEquals("Should return int", 
3813                       SymbolData.INT_TYPE.getInstanceData(), 
3814                       _etc.forArrayAccessOnly(aa, ad, SymbolData.INT_TYPE.getInstanceData()));
3815          assertEquals("Should now be 2 errors", 2, errors.size());
3816          assertEquals("Error message should be correct", 
3817                       "You cannot access an array element of a type name.  Perhaps you meant to create " +
3818                       "a new instance of int[]", 
3819                       errors.get(1).getFirst());
3820          
3821          // if type is not an array data, give appropriate error:
3822          assertEquals("Should return char", SymbolData.CHAR_TYPE.getInstanceData(), 
3823                       _etc.forArrayAccessOnly(aa, SymbolData.CHAR_TYPE.getInstanceData(), 
3824                                               SymbolData.INT_TYPE.getInstanceData()));
3825          assertEquals("Should now be 3 errors", 3, errors.size());
3826          assertEquals("Error message should be correct", 
3827                       "The variable referred to by this array access is a char, not an array", 
3828                       errors.get(2).getFirst());
3829          
3830          // If the array index is not an instance type, give error
3831          assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), 
3832                       _etc.forArrayAccessOnly(aa, ad.getInstanceData(), SymbolData.INT_TYPE));
3833          assertEquals("Should now be 4 errors", 4, errors.size());
3834          assertEquals("Error message should be correct", 
3835                       "You have used a type name in place of an array index.  Perhaps you meant to create " +
3836                       "a new instance of int", 
3837                       errors.get(3).getFirst());
3838          
3839          //If the array index is not an int, give error
3840          assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), 
3841                       _etc.forArrayAccessOnly(aa, ad.getInstanceData(), SymbolData.DOUBLE_TYPE.getInstanceData()));
3842          assertEquals("Should now be 5 errors", 5, errors.size());
3843          assertEquals("Error message should be correct", 
3844                       "You cannot reference an array element with an index of type double.  Instead, you must use an int",
3845                       errors.get(4).getFirst());   
3846        }
3847        
3848        
3849        public void testLiterals() {
3850          StringLiteral sl = new StringLiteral(SourceInfo.NONE, "string literal!");
3851          IntegerLiteral il = new IntegerLiteral(SourceInfo.NONE, 4);
3852          LongLiteral ll = new LongLiteral(SourceInfo.NONE, 5);
3853          FloatLiteral fl = new FloatLiteral(SourceInfo.NONE, 1.2f);
3854          DoubleLiteral dl = new DoubleLiteral(SourceInfo.NONE, 4.2);
3855          CharLiteral cl = new CharLiteral(SourceInfo.NONE, 'c');
3856          BooleanLiteral bl = new BooleanLiteral(SourceInfo.NONE, true);
3857          ClassLiteral csl = 
3858            new ClassLiteral(SourceInfo.NONE, new ClassOrInterfaceType(SourceInfo.NONE, "monkey", new Type[0]));
3859          
3860          SymbolData string = new SymbolData("java.lang.String");
3861          string.setIsContinuation(false);
3862          string.setPackage("java.lang");
3863          string.setMav(_publicMav);
3864          SymbolData classD = new SymbolData("java.lang.Class");
3865          classD.setIsContinuation(false);
3866          classD.setPackage("java.lang");
3867          classD.setMav(_publicMav);
3868          
3869          symbolTable.put("java.lang.String", string);
3870          symbolTable.put("java.lang.Class", classD);
3871          
3872          assertEquals("Should return string", string.getInstanceData(), sl.visit(_etc));
3873          assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), il.visit(_etc));
3874          assertEquals("Should return long", SymbolData.LONG_TYPE.getInstanceData(), ll.visit(_etc));
3875          assertEquals("Should return float", SymbolData.FLOAT_TYPE.getInstanceData(), fl.visit(_etc));
3876          assertEquals("Should return double", SymbolData.DOUBLE_TYPE.getInstanceData(), dl.visit(_etc));
3877          assertEquals("Should return char", SymbolData.CHAR_TYPE.getInstanceData(), cl.visit(_etc));
3878          assertEquals("Should return boolean", SymbolData.BOOLEAN_TYPE.getInstanceData(), bl.visit(_etc));
3879          assertEquals("Should return null type", SymbolData.NULL_TYPE.getInstanceData(), NULL_LITERAL.visit(_etc));
3880          assertEquals("Should return class", classD.getInstanceData(), _etc.forClassLiteralOnly(csl));
3881        }
3882        
3883        
3884        public void testForParenthesizedOnly() {
3885          Parenthesized p = new Parenthesized(SourceInfo.NONE, NULL_LITERAL);
3886          
3887          // if valueRes is an intance data, no problems
3888          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), 
3889                       _etc.forParenthesizedOnly(p, SymbolData.BOOLEAN_TYPE.getInstanceData()));
3890          assertEquals("Should be no errors", 0, errors.size());
3891          
3892          // if valueRes null, just return null
3893          assertEquals("Should return null", null, _etc.forParenthesizedOnly(p, null));
3894          assertEquals("Should be no errors", 0, errors.size());
3895          
3896          // if valueRes is package data, add error
3897          assertEquals("Should return null", null, _etc.forParenthesizedOnly(p, new PackageData("bob")));
3898          assertEquals("Should be 1 error", 1, errors.size());
3899          assertEquals("Error message should be correct","Could not resolve symbol bob" , errors.getLast().getFirst());
3900    
3901          // if value result not instance type, give error
3902          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), 
3903                       _etc.forParenthesizedOnly(p, SymbolData.INT_TYPE));
3904          assertEquals("Should be 2 errors", 2, errors.size());
3905          assertEquals("Error message should be correct",
3906                       "This class or interface name cannot appear in parentheses.  Perhaps you meant to create a new "
3907                         + "instance of int" , 
3908                       errors.getLast().getFirst());
3909          
3910          
3911        }
3912        
3913        public void testMethodInvocationHelper() {
3914          ParenthesizedExpressionList exp1 = new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]);
3915          MethodInvocation noArgs = new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), exp1);
3916          ParenthesizedExpressionList exp2 = 
3917            new ParenthesizedExpressionList(SourceInfo.NONE, 
3918                                            new Expression[]{new SimpleNameReference(SourceInfo.NONE, 
3919                                                                                     new Word(SourceInfo.NONE, "int"))});
3920          MethodInvocation typeArg = new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), exp2);
3921          ParenthesizedExpressionList exp3 =
3922            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new IntegerLiteral(SourceInfo.NONE, 5)});
3923          MethodInvocation oneIntArg =
3924            new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), exp3);
3925          ParenthesizedExpressionList exp4 =
3926            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new DoubleLiteral(SourceInfo.NONE, 4.2)});
3927          MethodInvocation oneDoubleArg = 
3928            new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), exp4);
3929          
3930          //should be able to match no args correctly
3931          MethodData noArgsM = 
3932            new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE, new VariableData[0], 
3933                           new String[0], _sd2, NULL_LITERAL);
3934          _sd2.addMethod(noArgsM);
3935          assertEquals("Should return boolean instance", 
3936                       SymbolData.BOOLEAN_TYPE.getInstanceData(), 
3937                       _etc.methodInvocationHelper(noArgs, _sd2.getInstanceData()));
3938          assertEquals("Should be no errors", 0, errors.size());
3939          
3940          //if no matching method, give error
3941          assertEquals("Should return null", null, _etc.methodInvocationHelper(oneIntArg, _sd2.getInstanceData()));
3942          assertEquals("Should be one error", 1, errors.size());
3943          assertEquals("Error message should be correct", "No method found in class " + _sd2.getName() 
3944                         + " with signature: myName(int).", 
3945                       errors.getLast().getFirst());
3946          
3947          // if matching method, but arg is not instance type, give error
3948          MethodData intArg = 
3949            new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.LONG_TYPE, 
3950                           new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, new String[0], _sd2, NULL_LITERAL);
3951          _sd2.addMethod(intArg);
3952          assertEquals("Should return long instance", SymbolData.LONG_TYPE.getInstanceData(), _etc.methodInvocationHelper(typeArg, _sd2.getInstanceData()));
3953          assertEquals("Should be 2 errors", 2, errors.size());
3954          assertEquals("Error message should be correct", "Cannot pass a class or interface name as an argument to a method.  Perhaps you meant to create an instance or use int.class", errors.getLast().getFirst());
3955          
3956          // if matching method, no error
3957          assertEquals("Should return long instance", SymbolData.LONG_TYPE.getInstanceData(), 
3958                       _etc.methodInvocationHelper(oneIntArg, _sd2.getInstanceData()));
3959          assertEquals("Should still be 2 errors", 2, errors.size());
3960          
3961          // non-static method from static context gives error
3962          assertEquals("Should return long instance", SymbolData.LONG_TYPE.getInstanceData(), 
3963                       _etc.methodInvocationHelper(oneIntArg, _sd2));
3964          assertEquals("Should be 3 errors", 3, errors.size());
3965          assertEquals("Error message should be correct", 
3966                       "Cannot access the non-static method myName from a static context", 
3967                       errors.getLast().getFirst());
3968          
3969          
3970          //static method from static context is okay
3971          MethodData doubleArg = 
3972            new MethodData("myName", _publicStaticMav, new TypeParameter[0], SymbolData.CHAR_TYPE, 
3973                           new VariableData[] {new VariableData(SymbolData.DOUBLE_TYPE)}, new String[0], _sd2, NULL_LITERAL);
3974          _sd2.addMethod(doubleArg);
3975          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), 
3976                       _etc.methodInvocationHelper(oneDoubleArg, _sd2));
3977          assertEquals("Should still be 3 errors", 3, errors.size());
3978        }
3979        
3980        public void testForSimpleMethodInvocation() {
3981          ParenthesizedExpressionList pel1 = new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]);
3982          MethodInvocation noArgs = new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), pel1);
3983          ParenthesizedExpressionList pel2 = 
3984            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new IntegerLiteral(SourceInfo.NONE, 5)});
3985          MethodInvocation oneIntArg = 
3986            new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), pel2);
3987          ParenthesizedExpressionList pel3 = 
3988            new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new DoubleLiteral(SourceInfo.NONE, 4.2)});
3989          MethodInvocation oneDoubleArg = 
3990            new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myName"), pel3);
3991          
3992          // if method not in class, give error
3993          assertEquals("Should return null", null, noArgs.visit(_etc));
3994          assertEquals("Should be 1 error", 1, errors.size());
3995          assertEquals("Error message should be correct", "No method found in class i.like.monkey with signature: myName().", 
3996                       errors.getLast().getFirst());
3997          
3998          //if method is in class, should work fine!
3999          MethodData noArgsM = 
4000            new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE, 
4001                           new VariableData[0], new String[0], _sd1, NULL_LITERAL);
4002          _sd1.addMethod(noArgsM);
4003          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), noArgs.visit(_etc));
4004          assertEquals("Should still just be 1 error", 1, errors.size());
4005          
4006          //should be able to reference a static method from instance context
4007          MethodData doubleArg = 
4008            new MethodData("myName", _publicStaticMav, new TypeParameter[0], SymbolData.CHAR_TYPE, 
4009                           new VariableData[] {new VariableData(SymbolData.DOUBLE_TYPE)}, new String[0], _sd1, NULL_LITERAL);
4010          _sd1.addMethod(doubleArg);
4011          
4012          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), oneDoubleArg.visit(_etc));
4013          assertEquals("Should still be just 1 error", 1, errors.size());
4014          
4015          //if in context of a static method, should be able to reference static method               
4016          _etc._data = doubleArg;
4017          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), oneDoubleArg.visit(_etc));
4018          assertEquals("Should still be just 1 error", 1, errors.size());
4019          
4020          //if in context of static method, should not be able to reference non-static method
4021          MethodData intArg = 
4022            new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.LONG_TYPE, 
4023                           new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, new String[0], _sd1, NULL_LITERAL);
4024          _sd1.addMethod(intArg);
4025          assertEquals("Should return long instance", SymbolData.LONG_TYPE.getInstanceData().getName(), 
4026                       oneIntArg.visit(_etc).getName());
4027          assertEquals("Should be 2 errors", 2, errors.size());
4028          assertEquals("Error message should be correct", "Cannot access the non-static method myName from a static context", 
4029                       errors.getLast().getFirst());
4030          
4031        }
4032        
4033        
4034        public void testForComplexMethodInvocation() {
4035          MethodInvocation staticNoArgs = 
4036            new ComplexMethodInvocation(SourceInfo.NONE, 
4037                                        new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "giraffe")),
4038                                        new Word(SourceInfo.NONE, "myName"), 
4039                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
4040          MethodInvocation noArgs = 
4041            new ComplexMethodInvocation(SourceInfo.NONE, 
4042                                        new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "g")), 
4043                                        new Word(SourceInfo.NONE, "myName"), 
4044                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
4045          MethodInvocation oneIntArg = 
4046            new ComplexMethodInvocation(SourceInfo.NONE, 
4047                                        new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "g")), 
4048                                        new Word(SourceInfo.NONE, "myName"), 
4049                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] { 
4050            new IntegerLiteral(SourceInfo.NONE, 5)}));
4051          MethodInvocation staticOneDoubleArg = 
4052            new ComplexMethodInvocation(SourceInfo.NONE, 
4053                                        new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "giraffe")),
4054                                        new Word(SourceInfo.NONE, "myName"), 
4055                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {
4056            new DoubleLiteral(SourceInfo.NONE, 4.2)}));
4057          MethodInvocation oneDoubleArg = 
4058            new ComplexMethodInvocation(SourceInfo.NONE, 
4059                                        new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "g")), 
4060                                        new Word(SourceInfo.NONE, "myName"), 
4061                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {
4062            new DoubleLiteral(SourceInfo.NONE, 4.2)}));
4063          
4064          SymbolData g = new SymbolData("giraffe");
4065          g.setIsContinuation(false);
4066          g.setMav(_publicMav);
4067          symbolTable.put("giraffe", g);
4068          
4069          VariableData var = new VariableData("g", _publicMav, g, true, _sd1);
4070          _etc._vars.addLast(var);
4071          
4072          //if method not in class, give error
4073          assertEquals("Should return null", null, noArgs.visit(_etc));
4074          assertEquals("Should be 1 error", 1, errors.size());
4075          assertEquals("Error message should be correct", "No method found in class giraffe with signature: myName().", 
4076                       errors.getLast().getFirst());
4077          
4078          // if method is in class, should work fine!
4079          MethodData noArgsM = new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE, 
4080                                              new VariableData[0], new String[0], g, NULL_LITERAL);
4081          g.addMethod(noArgsM);
4082          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), noArgs.visit(_etc));
4083          assertEquals("Should still just be 1 error", 1, errors.size());
4084          
4085          // should be able to reference a static method from instance context
4086          MethodData doubleArg = 
4087            new MethodData("myName", _publicStaticMav, new TypeParameter[0], SymbolData.CHAR_TYPE, 
4088                           new VariableData[] { new VariableData(SymbolData.DOUBLE_TYPE) }, 
4089                           new String[0], g, NULL_LITERAL);
4090          g.addMethod(doubleArg);
4091          
4092          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), oneDoubleArg.visit(_etc));
4093          assertEquals("Should still be just 1 error", 1, errors.size());
4094          
4095          // should be able to reference a static method from static context
4096          staticOneDoubleArg.visit(_etc);
4097          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), 
4098                       staticOneDoubleArg.visit(_etc));
4099          assertEquals("Should still be just 1 error", 1, errors.size());
4100          
4101          // should not be able to reference a non-static method from a static context
4102          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(),
4103                       staticNoArgs.visit(_etc));
4104          assertEquals("Should be 2 errors", 2, errors.size());
4105          assertEquals("Error message should be correct", 
4106                       "Cannot access the non-static method myName from a static context", 
4107                       errors.getLast().getFirst());
4108          
4109          //if in context of a static method, should be able to reference static method               
4110          _etc._data = doubleArg;
4111          var.setMav(_publicStaticMav);
4112          assertEquals("Should return char instance", SymbolData.CHAR_TYPE.getInstanceData(), oneDoubleArg.visit(_etc));
4113          assertEquals("Should still be just 2 errors", 2, errors.size());
4114          
4115          // if in context of static method, should be able to reference non-static method given a receiver
4116          MethodData intArg = 
4117            new MethodData("myName", _publicMav, new TypeParameter[0], SymbolData.LONG_TYPE, 
4118                           new VariableData[] { new VariableData(SymbolData.INT_TYPE)}, 
4119                           new String[0], g, NULL_LITERAL);
4120          g.addMethod(intArg);
4121          assertEquals("Should return long instance", SymbolData.LONG_TYPE.getInstanceData().getName(), 
4122                       oneIntArg.visit(_etc).getName());
4123          assertEquals("Should be 2 errors", 2, errors.size());
4124    //      assertEquals("Error message should be correct", "Cannot access the non-static method myName from a static context", 
4125    //      errors.getLast().getFirst());
4126          
4127          // if enclosing class is private, should not work!
4128          _etc._data = _sd1;
4129          g.setMav(_privateMav);
4130          noArgsM.setMav(_publicStaticMav);
4131          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), noArgs.visit(_etc));
4132          assertEquals("Should be 3 errors", 3, errors.size());
4133          assertEquals("Error message should be correct", 
4134                       "The class or interface giraffe in giraffe is private and cannot be accessed from i.like.monkey", 
4135                       errors.getLast().getFirst());
4136          
4137        }
4138        
4139        
4140        public void testCanBeAssigned() {
4141          VariableData finalWithValue = new VariableData("i", _finalMav, SymbolData.INT_TYPE, true, _sd1);
4142          VariableData finalWithOutValue = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, _sd1);
4143          VariableData notFinalWithValue = new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, _sd1);
4144          VariableData notFinalWithOutValue = new VariableData("i", _publicMav, SymbolData.INT_TYPE, false, _sd1);
4145          
4146          assertFalse("Should not be assignable", _etc.canBeAssigned(finalWithValue));
4147          assertTrue("Should be assignable", _etc.canBeAssigned(finalWithOutValue));
4148          assertTrue("Should be assignable", _etc.canBeAssigned(notFinalWithValue));
4149          assertTrue("Should be assignable", _etc.canBeAssigned(notFinalWithOutValue));
4150          
4151          
4152        }
4153        
4154        
4155        public void testForSimpleAssignment() {
4156          VariableData vd4 = new VariableData("Flanders", _publicMav, SymbolData.INT_TYPE, true, _sd4);
4157          VariableData vd5 = new VariableData("Ned", _publicMav, _sd4, true, _sd5);
4158          _sd5.addVar(vd5);
4159          _sd4.addVar(vd4);
4160          _etc._vars.add(vd5);
4161          _etc._data = _sd5;
4162          
4163          ComplexNameReference nf =
4164            new ComplexNameReference(SourceInfo.NONE, 
4165                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Ned")), 
4166                                     new Word(SourceInfo.NONE, "Flanders"));
4167          SimpleAssignmentExpression sa = 
4168            new SimpleAssignmentExpression(SourceInfo.NONE, nf, new IntegerLiteral(SourceInfo.NONE, 5));
4169          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa.visit(_etc));
4170          assertEquals("Should be 0 errors", 0, errors.size());
4171          
4172          //if variable is final, with a value cannot be reassigned
4173          vd4.gotValue();
4174          vd4.setMav(_finalPublicMav);
4175          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa.visit(_etc));
4176          assertEquals("Should now be 1 error", 1, errors.size());
4177          assertEquals("Error message should be correct", 
4178                       "You cannot assign a value to Flanders because it is immutable and has already been given a value",
4179                       errors.getLast().getFirst());
4180          
4181          // Test that an initialized value can be assigned to itself
4182          vd4.setMav(_publicMav);
4183          SimpleAssignmentExpression sa2 = new SimpleAssignmentExpression(SourceInfo.NONE, nf, nf);
4184          _sd4.setMav(_publicMav);
4185          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa2.visit(_etc));
4186          assertEquals("There should be 1 error", 1, errors.size());
4187          
4188          // Test that an uninitialized value cannot be assigned to itself as an initialization
4189          vd4.lostValue();
4190          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa2.visit(_etc));
4191          assertEquals("There should be 2 errors", 2, errors.size());
4192          assertEquals("The error message should be correct", 
4193                       "You cannot use Flanders here, because it may not have been given a value", 
4194                       errors.getLast().getFirst());
4195          
4196          // Test that a value cannot be assigned to a type
4197          SimpleAssignmentExpression sa3 = 
4198            new SimpleAssignmentExpression(SourceInfo.NONE, 
4199                                           new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")), 
4200                                           new IntegerLiteral(SourceInfo.NONE, 5));
4201          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa3.visit(_etc));
4202          assertEquals("Should be 3 errors", 3, errors.size());
4203          assertEquals("Error message should be correct", 
4204                       "You cannot assign a value to the type int.  Perhaps you meant to create a new instance of int", 
4205                       errors.getLast().getFirst());
4206          
4207          //Test that a type cannot be used on the rhs of an assignment
4208          SimpleAssignmentExpression sa4 = 
4209            new SimpleAssignmentExpression(SourceInfo.NONE, nf, 
4210                                           new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")));
4211          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), sa4.visit(_etc));
4212          assertEquals("Should be 4 errors", 4, errors.size());
4213          assertEquals("Error message should be correct", 
4214                       "You cannot use the type name int on the right hand side of an assignment.  Perhaps you meant to "
4215                         + "create a new instance of int", 
4216                       errors.getLast().getFirst());
4217          
4218          //test that we can assign to an array element
4219          LanguageLevelVisitor llv = 
4220            new LanguageLevelVisitor(_etc._file, 
4221                                     _etc._package, 
4222                                     null, // enclosingClassName for top level traversal
4223                                     _etc._importedFiles, 
4224                                     _etc._importedPackages, 
4225                                     new HashSet<String>(), 
4226                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
4227                                     new LinkedList<Command>());
4228    //      LanguageLevelConverter.symbolTable = llv.symbolTable = _etc.symbolTable;
4229    //      LanguageLevelConverter._newSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
4230          ArrayData boolArray = new ArrayData(SymbolData.BOOLEAN_TYPE, llv, SourceInfo.NONE);
4231          boolArray.setIsContinuation(false);
4232          symbolTable.remove("boolean[]");
4233          symbolTable.put("boolean[]", boolArray);
4234          VariableData myArrayVD = new VariableData("myArray", _publicMav, boolArray, true, _etc._data);
4235          _etc._vars.addLast(myArrayVD);
4236          
4237          SimpleNameReference snr = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "myArray"));
4238          SimpleAssignmentExpression sa5 = 
4239            new SimpleAssignmentExpression(SourceInfo.NONE, 
4240                                           new ArrayAccess(SourceInfo.NONE, snr, new IntegerLiteral(SourceInfo.NONE, 5)), 
4241                                           new BooleanLiteral(SourceInfo.NONE, true));
4242          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), sa5.visit(_etc));
4243          assertEquals("Should still be 4 errors", 4, errors.size());
4244          
4245          
4246          
4247        }
4248        
4249        public void testForPlusAssignmentExpression() {
4250          VariableData vd4 = new VariableData("Flanders", _publicMav, SymbolData.INT_TYPE, true, _sd4);
4251          VariableData vd5 = new VariableData("Ned", _publicMav, _sd4, true, _sd5);
4252          _sd5.addVar(vd5);
4253          _sd4.addVar(vd4);
4254          _etc._vars.add(vd5);
4255          _etc._data = _sd5;
4256          
4257          // Plus Assignment with numbers:
4258          // test that other assignment operators work correctly
4259          ComplexNameReference nf = 
4260            new ComplexNameReference(SourceInfo.NONE, 
4261                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Ned")), 
4262                                     new Word(SourceInfo.NONE, "Flanders"));
4263          PlusAssignmentExpression pa = 
4264            new PlusAssignmentExpression(SourceInfo.NONE, nf, new IntegerLiteral(SourceInfo.NONE, 5));
4265          
4266          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pa.visit(_etc));
4267          assertEquals("Should be 0 errors", 0, errors.size());
4268          
4269          // if variable does not have value, cannot be plus assigned
4270          vd4.lostValue();
4271          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pa.visit(_etc));
4272          assertEquals("Should now be 1 error", 1, errors.size());
4273          assertEquals("Error message should be correct", 
4274                       "You cannot use Flanders here, because it may not have been given a value",
4275                       errors.get(0).getFirst());
4276          
4277          // if variable is final, with a value cannot be reassigned
4278          vd4.gotValue();
4279          vd4.setMav(_finalPublicMav);
4280          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pa.visit(_etc));
4281          assertEquals("Should now be 2 errors", 2, errors.size());
4282          assertEquals("Error message should be correct", 
4283                       "You cannot assign a new value to Flanders because it is immutable and has already been given a value",
4284                       errors.get(1).getFirst());
4285          
4286          // Test that an initialized value can be assigned to itself
4287          vd4.setMav(_publicMav);
4288          _sd4.setMav(_publicMav);
4289          PlusAssignmentExpression pa2 = new PlusAssignmentExpression(SourceInfo.NONE, nf, nf);
4290          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pa2.visit(_etc));
4291          assertEquals("There should still be 2 errors", 2, errors.size());
4292          
4293          // Test that an uninitialized value cannot be assigned to itself as an initialization
4294          vd4.lostValue();
4295          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), pa2.visit(_etc));
4296          assertEquals("There should still be 2 errors", 2, errors.size());  // Generated two duplicate messages.
4297          assertEquals("The first error message should be correct", 
4298                       "You cannot use Flanders here, because it may not have been given a value", 
4299                       errors.get(0).getFirst());
4300          
4301          //test Plus Assignment for String concatanation
4302          SymbolData stringSD = new SymbolData("java.lang.String");
4303          stringSD.setIsContinuation(false);
4304          stringSD.setPackage("java.lang");
4305          symbolTable.remove("java.lang.String");
4306          symbolTable.put("java.lang.String", stringSD);
4307          VariableData s = new VariableData("s", _publicMav, stringSD, true, _etc._data);
4308          _etc._vars.add(s);
4309          
4310          SimpleNameReference sRef = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s"));
4311          //test string concatenation, where the string is first.
4312          PlusAssignmentExpression pa3 = 
4313            new PlusAssignmentExpression(SourceInfo.NONE, sRef, new BooleanLiteral(SourceInfo.NONE, true));
4314          TypeData result =  pa3.visit(_etc);
4315          assertEquals("string concatenation with string at the front.  Should return String type", 
4316                       stringSD.getInstanceData(), 
4317                       pa3.visit(_etc));
4318          assertEquals("Should still be 2 errors", 2, errors.size());
4319          
4320          // when both sides are strings
4321          PlusAssignmentExpression pa4 = 
4322            new PlusAssignmentExpression(SourceInfo.NONE, sRef, new StringLiteral(SourceInfo.NONE, "cat"));
4323          assertEquals("string concatenation with string on both sides.  Should return String type", 
4324                       stringSD.getInstanceData(), pa4.visit(_etc));
4325          assertEquals("Should still be 2 errors", 2, errors.size());
4326          
4327          // when string is second
4328          vd4.gotValue();
4329          PlusAssignmentExpression pa5 = 
4330            new PlusAssignmentExpression(SourceInfo.NONE, nf, new StringLiteral(SourceInfo.NONE, "house "));
4331          assertEquals("string + concatenation with string at back.  Should give error", 
4332                       stringSD.getInstanceData(), 
4333                       pa5.visit(_etc));
4334          assertEquals("Should now be 3 errors", 3, errors.size());
4335          assertEquals("Error message should be correct", 
4336                       "The arguments to the Plus Assignment Operator (+=) must either include an instance of a String " + 
4337                       "or both be numbers.  You have specified arguments of type int and java.lang.String", 
4338                       errors.get(2).getFirst());
4339        }
4340        
4341        public void testForNumericAssignmentExpression() {
4342          VariableData vd4 = new VariableData("Flanders", _publicMav, SymbolData.INT_TYPE, true, _sd4);
4343          VariableData vd5 = new VariableData("Ned", _publicMav, _sd4, true, _sd5);
4344          _sd5.addVar(vd5);
4345          _sd4.addVar(vd4);
4346          _etc._vars.add(vd5);
4347          _etc._data = _sd5;
4348          
4349          // test that numeric assignment with good values on left and right works correctly
4350          ComplexNameReference nf = 
4351            new ComplexNameReference(SourceInfo.NONE, 
4352                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Ned")), 
4353                                     new Word(SourceInfo.NONE, "Flanders"));
4354          NumericAssignmentExpression na = 
4355            new MinusAssignmentExpression(SourceInfo.NONE, nf, new IntegerLiteral(SourceInfo.NONE, 5));
4356          
4357          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), na.visit(_etc));
4358          assertEquals("Should be 0 errors", 0, errors.size());
4359          
4360          //if variable does not have value, cannot be plus assigned
4361          vd4.lostValue();
4362          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), na.visit(_etc));
4363          assertEquals("Should now be 1 error", 1, errors.size());
4364          assertEquals("Error message should be correct", 
4365                       "You cannot use Flanders here, because it may not have been given a value",
4366                       errors.get(0).getFirst());
4367          
4368          //if variable is final, cannot be reassigned
4369          vd4.gotValue();
4370          vd4.setMav(_finalPublicMav);
4371          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), na.visit(_etc));
4372          assertEquals("Should now be 2 errors", 2, errors.size());
4373          assertEquals("Error message should be correct", 
4374                       "You cannot assign a new value to Flanders because it is immutable and has already been given a value",
4375                       errors.get(1).getFirst());
4376          
4377          // Test that an initialized value can be assigned to itself
4378          vd4.setMav(_publicMav);
4379          _sd4.setMav(_publicMav);
4380          NumericAssignmentExpression na2 = new ModAssignmentExpression(SourceInfo.NONE, nf, nf);
4381          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), na2.visit(_etc));
4382          assertEquals("There should be 2 errors", 2, errors.size());
4383          
4384          // Test that an uninitialized value cannot be assigned to itself as an initialization
4385          vd4.lostValue();
4386          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), na2.visit(_etc));
4387          assertEquals("There should still be 2 errors", 2, errors.size());  // Generated duplicate error message
4388          assertEquals("The new error message should be correct", 
4389                       "You cannot use Flanders here, because it may not have been given a value", 
4390                       errors.get(0).getFirst()); 
4391        }
4392        
4393        public void testForIncrementExpression() {
4394          VariableData vd4 = new VariableData("Flanders", _publicMav, SymbolData.INT_TYPE, true, _sd4);
4395          VariableData vd5 = new VariableData("Ned", _publicMav, _sd4, true, _sd5);
4396          _sd5.addVar(vd5);
4397          _sd4.addVar(vd4);
4398          _etc._vars.add(vd5);
4399          _etc._data = _sd5;
4400          
4401          //test that words with a pre-increment operator before only work if they already have a value and aren't final.
4402          ComplexNameReference nf = 
4403            new ComplexNameReference(SourceInfo.NONE, 
4404                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Ned")), 
4405                                     new Word(SourceInfo.NONE, "Flanders"));
4406          PositivePrefixIncrementExpression ppi = new PositivePrefixIncrementExpression(SourceInfo.NONE, nf);
4407          
4408          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ppi.visit(_etc));
4409          assertEquals("Should still be 0 errors", 0, errors.size());
4410          
4411          // test that attempting to increment the value of a field that doesn't have a value will throw an error
4412          vd4.lostValue();
4413          assertEquals("Should return int instance.", SymbolData.INT_TYPE.getInstanceData(), ppi.visit(_etc));
4414          assertEquals("Should now be 1 errors", 1, errors.size());
4415          assertEquals("Error message should be correct", 
4416                       "You cannot use Flanders here, because it may not have been given a value",
4417                       errors.get(0).getFirst());
4418          
4419          // test that attempting to increment the value of a final field will throw an error
4420          vd4.gotValue();
4421          vd4.setMav(_finalPublicMav);
4422          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ppi.visit(_etc));
4423          assertEquals("Should now be 2 errors", 2, errors.size());
4424          assertEquals("Error message should be correct", 
4425                       "You cannot assign a new value to Flanders because it is immutable and has already been given a value",
4426                       errors.get(1).getFirst());
4427          
4428          // Check that ++int doesn't work
4429          PositivePrefixIncrementExpression ppi2 = 
4430            new PositivePrefixIncrementExpression(SourceInfo.NONE, 
4431                                                  new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")));
4432          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ppi2.visit(_etc));
4433          assertEquals("There should now be 3 errors", 3, errors.size());
4434          assertEquals("The error message should be correct", 
4435                       "You cannot increment or decrement int, because it is a class name not an instance.  " +
4436                       "Perhaps you meant to create a new instance of int", 
4437                       errors.get(2).getFirst());
4438          
4439          
4440          // Check that ++(int) doesn't work
4441          Parenthesized p1 = 
4442            new Parenthesized(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")));
4443          PositivePrefixIncrementExpression ppi3 = new PositivePrefixIncrementExpression(SourceInfo.NONE, p1);
4444          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ppi3.visit(_etc));
4445          assertEquals("There should now be 4 errors", 4, errors.size());  // Generated error is not a duplicate
4446          assertEquals("The error message should be correct", 
4447                       "You cannot increment or decrement int, because it is a class name not an instance.  " +
4448                       "Perhaps you meant to create a new instance of int",
4449                       errors.get(3).getFirst());
4450          
4451          
4452          // Test that words with post-decrement operator only work if they already have a value and aren't final.
4453          vd4.setMav(_publicMav);
4454          NegativePostfixIncrementExpression npi = new NegativePostfixIncrementExpression(SourceInfo.NONE, nf);
4455          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), npi.visit(_etc));
4456          assertEquals("Should still be 4 errors", 4, errors.size());
4457          
4458          // Test that attempting to decrement the value of a field that doesn't have a value will throw an error
4459          vd4.lostValue();
4460          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), npi.visit(_etc));
4461          assertEquals("Should still be 4 errors", 4, errors.size());
4462          assertEquals("Error message should be correct", 
4463                       "You cannot use Flanders here, because it may not have been given a value",
4464                       errors.get(0).getFirst());      
4465          
4466          // test that attempting to increment the value of a final field will throw an error
4467          vd4.gotValue();
4468          vd4.setMav(_finalPublicMav);
4469          assertEquals("Should return int instance.", SymbolData.INT_TYPE.getInstanceData(), npi.visit(_etc));
4470          assertEquals("Should still be 4 errors", 4, errors.size());
4471          assertEquals("Error message should be correct", 
4472                       "You cannot assign a new value to Flanders because it is immutable and has already been given a value",
4473                       errors.get(1).getFirst());
4474          
4475          
4476          // Check that int-- doesn't work
4477          NegativePostfixIncrementExpression npi2 = 
4478            new NegativePostfixIncrementExpression(SourceInfo.NONE, 
4479                                                   new SimpleNameReference(SourceInfo.NONE, 
4480                                                                           new Word(SourceInfo.NONE, "int")));
4481          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), npi2.visit(_etc));
4482          assertEquals("There should be 5 errors", 5, errors.size());
4483          assertEquals("The error message should be correct", 
4484                       "You cannot increment or decrement int, because it is a class name not an instance.  Perhaps you " + 
4485                       "meant to create a new instance of int", 
4486                       errors.get(4).getFirst());
4487          
4488          // Check that (int)-- doesn't work
4489          Parenthesized p2 = 
4490            new Parenthesized(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")));
4491          NegativePostfixIncrementExpression npi3 = new NegativePostfixIncrementExpression(SourceInfo.NONE, p2);
4492          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), npi3.visit(_etc));
4493          assertEquals("There should be 6 errors", 6, errors.size());  // Wny isn't this a duplicate of error #4?
4494          assertEquals("The error message should be correct", 
4495                       "You cannot increment or decrement int, because it is a class name not an instance.  " + 
4496                       "Perhaps you meant to create a new instance of int", 
4497                       errors.get(5).getFirst());
4498          
4499          
4500          //should break: double increment/decrement ++(--Ned.Flanders)
4501          vd4.setMav(_publicMav);
4502          Parenthesized p3 = new Parenthesized(SourceInfo.NONE, new NegativePrefixIncrementExpression(SourceInfo.NONE, nf));
4503          PositivePrefixIncrementExpression ppi4 = new PositivePrefixIncrementExpression(SourceInfo.NONE, p3);
4504          assertEquals("Should return null", null, ppi4.visit(_etc));
4505          assertEquals("Should have added 1 error", 7, errors.size());
4506          assertEquals("Should have correct error message",
4507                       "You cannot assign a value to an increment expression", 
4508                       errors.getLast().getFirst());
4509          
4510    //      //should break: non number being incremented
4511          VariableData s = new VariableData("s", _publicMav, SymbolData.BOOLEAN_TYPE, true, _etc._data);
4512          _etc._vars.addLast(s);
4513          PositivePrefixIncrementExpression ppi5 = 
4514            new PositivePrefixIncrementExpression(SourceInfo.NONE, 
4515                                                  new SimpleNameReference(SourceInfo.NONE, 
4516                                                                          new Word(SourceInfo.NONE, "s")));
4517          assertEquals("Should return boolean instance", SymbolData.BOOLEAN_TYPE.getInstanceData(), ppi5.visit(_etc));
4518          assertEquals("Should have added 1 error", 8, errors.size());
4519          assertEquals("Should have correct error message", 
4520                       "You cannot increment or decrement something that is not a number type.  You have specified " +
4521                       "something of type boolean", errors.get(7).getFirst());
4522          
4523          //nested parentheses...should work
4524          PositivePrefixIncrementExpression ppi6 = 
4525            new PositivePrefixIncrementExpression(SourceInfo.NONE, new Parenthesized(SourceInfo.NONE, 
4526                                                                                     new Parenthesized(SourceInfo.NONE, nf)));
4527          assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), ppi6.visit(_etc));
4528          assertEquals("Should still be 8 errors", 8, errors.size());
4529        }
4530        
4531        
4532        public void testForSimpleAnonymousClassInstantiation() {
4533          ClassOrInterfaceType objType = new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]);
4534          AnonymousClassInstantiation basic = 
4535            new SimpleAnonymousClassInstantiation(SourceInfo.NONE, 
4536                                                  objType, 
4537                                                  new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]),
4538                                                  new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
4539          
4540          SymbolData object = LanguageLevelConverter.symbolTable.get("java.lang.Object");
4541          
4542          _sd1.setAnonymousInnerClassNum(0);
4543          
4544          // Once our enclosing data does have an anonymous inner class, it's okay to look it up
4545          SymbolData anon1 = new SymbolData("i.like.monkey$1");
4546          anon1.setIsContinuation(false);
4547          anon1.setPackage("i.like");
4548          anon1.setMav(_publicMav);
4549          anon1.setOuterData(_sd1);
4550          assert object != null;
4551          anon1.setSuperClass(object);
4552          _sd1.addInnerClass(anon1);
4553    //      System.err.println("****** anon1 is: " + anon1);
4554    //      System.err.println("****** instance data = " + anon1.getInstanceData());
4555          assertEquals("Should return anon1 instance", anon1.getInstanceData(), basic.visit(_etc));
4556          
4557          assertEquals("Should be no errors", 0, errors.size());
4558          
4559          VariableDeclaration vdecl = new VariableDeclaration(SourceInfo.NONE,
4560                                                              _packageMav,
4561                                                              new VariableDeclarator[] {
4562            new UninitializedVariableDeclarator(SourceInfo.NONE, 
4563                                                new PrimitiveType(SourceInfo.NONE, "double"), 
4564                                                new Word (SourceInfo.NONE, "field1")),
4565              new UninitializedVariableDeclarator(SourceInfo.NONE, 
4566                                                  new PrimitiveType(SourceInfo.NONE, "boolean"), 
4567                                                  new Word (SourceInfo.NONE, "field2"))});      
4568          
4569          PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
4570          UninitializedVariableDeclarator uvd = 
4571            new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
4572          FormalParameter param = 
4573            new FormalParameter(SourceInfo.NONE, 
4574                                new UninitializedVariableDeclarator(SourceInfo.NONE, intt, 
4575                                                                    new Word(SourceInfo.NONE, "j")), false);
4576          BracedBody bb = 
4577            new BracedBody(SourceInfo.NONE, 
4578                           new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE,  _packageMav, 
4579                                                                    new UninitializedVariableDeclarator[]{uvd}), 
4580                             new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
4581          
4582          ConcreteMethodDef cmd1 = 
4583            new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], 
4584                                  intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param}, 
4585                                  new ReferenceType[0], bb);
4586          BracedBody classBb = new BracedBody(SourceInfo.NONE, new BodyItemI[] { vdecl, cmd1 });
4587          
4588          SimpleAnonymousClassInstantiation  complicated = 
4589            new SimpleAnonymousClassInstantiation(SourceInfo.NONE, 
4590                                                  new ClassOrInterfaceType(SourceInfo.NONE, "name", new Type[0]), 
4591                                                  new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), 
4592                                                  classBb);
4593          SymbolData sd = new SymbolData("name");
4594          sd.setIsContinuation(false);
4595          sd.setMav(_publicMav);
4596          symbolTable.put("name", sd);
4597          SymbolData anon2 = new SymbolData("i.like.monkey$2");
4598          anon2.setIsContinuation(false);
4599          anon2.setPackage("i.like");
4600          anon2.setSuperClass(sd);
4601          anon2.setOuterData(_sd1);
4602          _sd1.addInnerClass(anon2);
4603          
4604          VariableData vd1 = new VariableData("field1", _publicMav, SymbolData.DOUBLE_TYPE, true, sd);
4605          VariableData vd2 = new VariableData("field2", _publicMav, SymbolData.DOUBLE_TYPE, true, sd);
4606          sd.addVar(vd1);
4607          sd.addVar(vd2);
4608          
4609          MethodData md = 
4610            new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[] {
4611            new VariableData("j", _finalMav, SymbolData.INT_TYPE, true, null)}, new String[0], sd, cmd1);
4612          md.getParams()[0].setEnclosingData(md);
4613          MethodData cd = 
4614            new MethodData("name", _publicMav, new TypeParameter[0], sd, new VariableData[0], new String[0], sd, cmd1);
4615          anon2.addMethod(md);
4616          sd.addMethod(cd);
4617          // check that this complex expression returns correct type, overwriting fields and method in super class
4618          assertEquals("Should return anon2.  ", anon2.getInstanceData(), complicated.visit(_etc));
4619          assertEquals("There should be no errors", 0, errors.size());
4620          
4621          _etc._data.addVar(new VariableData("myAnon", _publicMav, sd, false, _etc._data));
4622          
4623          
4624          // Test that I can assign the anonymous inner class to a variable of the right type.
4625          _sd1.setAnonymousInnerClassNum(1);
4626          symbolTable.put("int", SymbolData.INT_TYPE);
4627          VariableDeclaration vd =
4628            new VariableDeclaration(SourceInfo.NONE, _publicMav, new VariableDeclarator[] { 
4629            new InitializedVariableDeclarator(SourceInfo.NONE, 
4630                                              new ClassOrInterfaceType(SourceInfo.NONE, "name", new Type[0]), 
4631                                              new Word(SourceInfo.NONE, "myAnon"), complicated)});
4632          vd.visit(_etc);
4633          assertEquals("There should still be no errors", 0, errors.size());
4634          
4635          //Test that a method invoked from an anonymous inner class does its thing correctly
4636          _sd1.setAnonymousInnerClassNum(1);
4637          MethodInvocation mie = 
4638            new ComplexMethodInvocation(SourceInfo.NONE, 
4639                                        complicated, 
4640                                        new Word(SourceInfo.NONE, "myMethod"),
4641                                        new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] { 
4642            new IntegerLiteral(SourceInfo.NONE, 5)}));
4643          assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), mie.visit(_etc));
4644          assertEquals("There should still be no errors", 0, errors.size());
4645          
4646          // Test that we can get a field from an anonymous inner class
4647          _sd1.setAnonymousInnerClassNum(1);
4648          
4649          Expression nr = new ComplexNameReference(SourceInfo.NONE, complicated, new Word(SourceInfo.NONE, "field1"));
4650          assertEquals("Should return double", SymbolData.DOUBLE_TYPE.getInstanceData(), nr.visit(_etc));
4651          assertEquals("There should be no errors...still!", 0, errors.size());
4652          
4653          // Let sd be abstract with an abstract method that our instantiation doesn't override.  Should throw an error.
4654          _sd1.setAnonymousInnerClassNum(1);
4655          sd.setMav(_publicAbstractMav);
4656          sd.addMethod(new MethodData("yeah", _abstractMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE, 
4657                                      new VariableData[0], new String[0], sd, cmd1));
4658          
4659          assertEquals("Should return anon2 instance", anon2.getInstanceData(), complicated.visit(_etc));
4660          assertEquals("There should be one error", 1, errors.size());
4661          assertEquals("The error message should be correct", 
4662                       "This anonymous inner class must override the abstract method: yeah() in name", 
4663                       errors.get(0).getFirst());
4664          
4665          //cannot use syntax new A.B() if B is not static.  Make sure appropriate error is thrown.
4666          SimpleAnonymousClassInstantiation nestedNonStatic = 
4667            new SimpleAnonymousClassInstantiation(SourceInfo.NONE, 
4668                                                  new ClassOrInterfaceType(SourceInfo.NONE, "A.B", new Type[0]), 
4669                                                  new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), 
4670                                                  new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
4671          
4672          SymbolData a = new SymbolData("A");
4673          a.setIsContinuation(false);
4674          SymbolData b = new SymbolData("A$B");
4675          b.setIsContinuation(false);
4676          b.setOuterData(a);
4677          a.addInnerClass(b);
4678          MethodData consb = new MethodData("B", _publicMav, new TypeParameter[0], b, 
4679                                            new VariableData[0], 
4680                                            new String[0], 
4681                                            b,
4682                                            null);
4683          b.addMethod(consb);
4684          symbolTable.put("A", a);
4685          a.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
4686          b.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
4687          
4688          SymbolData anon3 = new SymbolData("i.like.monkey$3");
4689          anon3.setIsContinuation(false);
4690          anon3.setMav(_publicMav);
4691          _sd1.addInnerClass(anon3);
4692          anon3.setOuterData(_sd1);
4693          
4694          //if inner part is not static, give error
4695          assertEquals("Should return anon3", anon3.getInstanceData(), nestedNonStatic.visit(_etc));
4696          assertEquals("Should be 2 errors", 2, errors.size());
4697          
4698          assertEquals("Error message should be correct", 
4699                       "A.B is not a static inner class, and thus cannot be instantiated from this context.  "
4700                         + "Perhaps you meant to use an instantiation of the form new A().new B()",
4701                       errors.getLast().getFirst());
4702          
4703          _sd1.setAnonymousInnerClassNum(2);
4704          //if inner part is static, no problem
4705          b.addModifier("static");
4706          assertEquals("Should return anon3", anon3.getInstanceData(), nestedNonStatic.visit(_etc));
4707          assertEquals("Should still be just 2 errors", 2, errors.size());
4708          
4709        }
4710        
4711        public void testForComplexAnonymousClassInstantiation() {
4712          ClassOrInterfaceType objType = new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]);
4713          
4714          AnonymousClassInstantiation basic = 
4715            new ComplexAnonymousClassInstantiation(SourceInfo.NONE, 
4716                                                   new SimpleNameReference(SourceInfo.NONE, 
4717                                                                           new Word(SourceInfo.NONE, "bob")),
4718                                                   objType, 
4719                                                   new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]),
4720                                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
4721          
4722          // Create a variable 'bob' of type _sd2 within _sd1
4723          VariableData bob = new VariableData("bob", _publicMav, _sd2, true, _sd1);
4724          _etc._vars.add(bob);  // _data for _etc is _sd1
4725          
4726          SymbolData object = LanguageLevelConverter.symbolTable.get("java.lang.Object");
4727          _sd1.setAnonymousInnerClassNum(0);
4728          SymbolData anon1 = new SymbolData("i.like.monkey$1");
4729          anon1.setIsContinuation(false);
4730          anon1.setPackage("i.like");
4731          anon1.setMav(_publicMav);
4732          anon1.setOuterData(_sd1);
4733          assert object != null;
4734          anon1.setSuperClass(object);
4735          _sd1.addInnerClass(anon1);
4736          assertEquals("Should return anon1 instance", anon1.getInstanceData(), basic.visit(_etc));
4737          
4738          assertEquals("Should be no errors", 0, errors.size());
4739          
4740          
4741          VariableDeclaration vdecl = new VariableDeclaration(SourceInfo.NONE,
4742                                                              _packageMav,
4743                                                              new VariableDeclarator[] {
4744            new UninitializedVariableDeclarator(SourceInfo.NONE, 
4745                                                new PrimitiveType(SourceInfo.NONE, "double"), 
4746                                                new Word (SourceInfo.NONE, "field1")),
4747              new UninitializedVariableDeclarator(SourceInfo.NONE, 
4748                                                  new PrimitiveType(SourceInfo.NONE, "boolean"), 
4749                                                  new Word (SourceInfo.NONE, "field2"))});      
4750          
4751          PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
4752          UninitializedVariableDeclarator uvd = 
4753            new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
4754          FormalParameter param = 
4755            new FormalParameter(SourceInfo.NONE, 
4756                                new UninitializedVariableDeclarator(SourceInfo.NONE, intt, 
4757                                                                    new Word(SourceInfo.NONE, "j")), false);
4758          BracedBody bb = 
4759            new BracedBody(SourceInfo.NONE, new BodyItemI[] {
4760            new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), 
4761              new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
4762          
4763          ConcreteMethodDef cmd1 = 
4764            new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], 
4765                                  intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param}, 
4766                                  new ReferenceType[0], bb);
4767          BracedBody classBb = new BracedBody(SourceInfo.NONE, new BodyItemI[] { vdecl, cmd1 });
4768          
4769          ComplexAnonymousClassInstantiation  complicated = 
4770            new ComplexAnonymousClassInstantiation(SourceInfo.NONE, 
4771                                                   new SimpleNameReference(SourceInfo.NONE, 
4772                                                                           new Word(SourceInfo.NONE, "bob")),
4773                                                   new ClassOrInterfaceType(SourceInfo.NONE, "name", new Type[0]), 
4774                                                   new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), 
4775                                                   classBb);
4776          
4777          // This test is not well-documented.  In refactoring, I tried to preserve it as best as possible.
4778          SymbolData sd = new SymbolData("name");
4779          sd.setIsContinuation(false);
4780          sd.setMav(_publicMav);
4781          sd.setSuperClass(object);
4782          symbolTable.put("name", sd);
4783          SymbolData anon2 = new SymbolData("i.like.monkey$2");
4784          anon2.setIsContinuation(false);
4785          anon2.setPackage("i.like");
4786          anon2.setSuperClass(sd);
4787          anon2.setOuterData(_sd1);
4788          _sd1.addInnerClass(anon2);
4789          
4790          VariableData vd1 = new VariableData("field1", _publicMav, SymbolData.DOUBLE_TYPE, true, sd);
4791          VariableData vd2 = new VariableData("field2", _publicMav, SymbolData.DOUBLE_TYPE, true, sd);
4792          sd.addVar(vd1);
4793          sd.addVar(vd2);
4794          
4795          MethodData md = 
4796            new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[] {
4797            new VariableData("j", _finalMav, SymbolData.INT_TYPE, true, null)}, new String[0], sd, cmd1);
4798          md.getParams()[0].setEnclosingData(md);
4799          MethodData cd = 
4800            new MethodData("name", _publicMav, new TypeParameter[0], sd, new VariableData[0], new String[0], sd, cmd1);
4801          anon2.addMethod(md);
4802          sd.addMethod(cd);
4803          // check that this complex expression returns correct type, overwriting fields in super class and method in 
4804          // super class
4805          assertEquals("Should return anon2.  ", anon2.getInstanceData(), complicated.visit(_etc));
4806          assertEquals("There should be no errors", 0, errors.size());
4807          
4808          _etc._data.addVar(new VariableData("myAnon", _publicMav, sd, false, _etc._data));
4809          
4810          
4811          // Test that I can assign the anonymous inner class to a variable of the right type.
4812          _sd1.setAnonymousInnerClassNum(1);
4813          symbolTable.put("int", SymbolData.INT_TYPE);
4814          VariableDeclaration vd = 
4815            new VariableDeclaration(SourceInfo.NONE, _publicMav, new VariableDeclarator[] { 
4816            new InitializedVariableDeclarator(SourceInfo.NONE, 
4817                                              new ClassOrInterfaceType(SourceInfo.NONE, "name", new Type[0]),
4818                                              new Word(SourceInfo.NONE, "myAnon"), 
4819                                              complicated)});
4820          vd.visit(_etc);
4821          assertEquals("There should still be no errors", 0, errors.size());
4822          
4823          //Test that a method invoked from an anonymous inner class does its thing correctly
4824          _sd1.setAnonymousInnerClassNum(1);
4825          MethodInvocation mie = 
4826            new ComplexMethodInvocation(SourceInfo.NONE, complicated, 
4827                                        new Word(SourceInfo.NONE, "myMethod"),
4828                                        new ParenthesizedExpressionList(SourceInfo.NONE, 
4829                                                                        new Expression[] { 
4830            new IntegerLiteral(SourceInfo.NONE, 5)}));
4831          assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), mie.visit(_etc));
4832          assertEquals("There should still be no errors", 0, errors.size());
4833          
4834    //      //Test that we can get a field from an anonymous inner class
4835          _sd1.setAnonymousInnerClassNum(1);
4836          
4837          Expression nr = new ComplexNameReference(SourceInfo.NONE, complicated, new Word(SourceInfo.NONE, "field1"));
4838          assertEquals("Should return double", SymbolData.DOUBLE_TYPE.getInstanceData(), nr.visit(_etc));
4839          assertEquals("There should be no errors...still!", 0, errors.size());
4840          
4841          // If the implemented sd is abstract and it isn't overriden, type-checking should throw an error.
4842          _sd1.setAnonymousInnerClassNum(1);
4843          sd.setMav(_publicAbstractMav);
4844          sd.addMethod(new MethodData("yeah", _abstractMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE, 
4845                                      new VariableData[0], new String[0], sd, cmd1));
4846          
4847          assertEquals("Should return anon2 instance", anon2.getInstanceData(), complicated.visit(_etc));
4848          assertEquals("There should be one error", 1, errors.size());
4849          assertEquals("The error message should be correct", 
4850                       "This anonymous inner class must override the abstract method: yeah() in name", 
4851                       errors.get(0).getFirst());
4852          
4853    //      //cannot use syntax a.new B() if B is static.  Make sure appropriate error is thrown.
4854          ComplexAnonymousClassInstantiation nestedNonStatic = 
4855            new ComplexAnonymousClassInstantiation(SourceInfo.NONE, 
4856                                                   new SimpleNameReference(SourceInfo.NONE, 
4857                                                                           new Word(SourceInfo.NONE, "a")),
4858                                                   new ClassOrInterfaceType(SourceInfo.NONE, "B", new Type[0]), 
4859                                                   new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), 
4860                                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
4861          
4862          SymbolData a = new SymbolData("A");
4863          a.setIsContinuation(false);
4864          SymbolData b = new SymbolData("A$B");
4865          b.setIsContinuation(false);
4866          b.setOuterData(a);
4867          a.addInnerClass(b);
4868          MethodData consb = 
4869            new MethodData("B", _publicMav, new TypeParameter[0], b, new VariableData[0], new String[0], b, null);
4870          b.addMethod(consb);
4871          symbolTable.put("A", a);
4872          a.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
4873          b.setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}));
4874          
4875          SymbolData anon3 = new SymbolData("i.like.monkey$3");
4876          anon3.setIsContinuation(false);
4877          anon3.setMav(_publicMav);
4878          _sd1.addInnerClass(anon3);
4879          anon3.setOuterData(_sd1);
4880          VariableData aVar = new VariableData("a", _publicMav, a, true, _sd1);
4881          _etc._vars.add(aVar);
4882          
4883          //if inner part is not static, no problem
4884          assertEquals("Should return anon3", anon3.getInstanceData(), nestedNonStatic.visit(_etc));
4885          assertEquals("Should still be just 1 error", 1, errors.size());      
4886          
4887          //if outer part is private, should break
4888          _sd1.setAnonymousInnerClassNum(2);
4889          a.setMav(_privateMav);
4890          assertEquals("Should return anon3", anon3.getInstanceData(), nestedNonStatic.visit(_etc));
4891          assertEquals("Should be 2 errors", 2, errors.size());
4892          assertEquals("Error message should be correct", 
4893                       "The class or interface A in A is private and cannot be accessed from i.like.monkey", 
4894                       errors.getLast().getFirst());
4895          a.setMav(_publicMav);
4896          
4897          //if inner part is static, give error
4898          _sd1.setAnonymousInnerClassNum(2);
4899          b.setMav(_publicStaticMav);
4900          assertEquals("Should return anon3", anon3.getInstanceData(), nestedNonStatic.visit(_etc));
4901          assertEquals("Should be 3 errors", 3, errors.size());
4902          assertEquals("Error message should be correct", 
4903                       "You cannot instantiate a static inner class or interface with this syntax.  Instead, try new A.B()", 
4904                       errors.getLast().getFirst());
4905          
4906          // if inner part is not static, but outer part is type name, give error
4907          ComplexAnonymousClassInstantiation nested = 
4908            new ComplexAnonymousClassInstantiation(SourceInfo.NONE, 
4909                                                   new SimpleNameReference(SourceInfo.NONE, 
4910                                                                           new Word(SourceInfo.NONE, "A")),
4911                                                   new ClassOrInterfaceType(SourceInfo.NONE, "B", new Type[0]), 
4912                                                   new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), 
4913                                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
4914          _sd1.setAnonymousInnerClassNum(2);
4915          b.setMav(_publicMav);
4916          assertEquals("Should return anon3", anon3.getInstanceData(), nested.visit(_etc));
4917          assertEquals("Should be 4 errors", 4, errors.size());
4918          assertEquals("Error message should be correct", 
4919                       "The constructor of a non-static inner class can only be called on an instance of its "
4920                         + "containing class (e.g. new A().new B())", 
4921                       errors.getLast().getFirst());
4922          
4923        }
4924      }
4925    }