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 }