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 java.util.*;
042 import java.io.File;
043 import edu.rice.cs.plt.reflect.JavaVersion;
044 import edu.rice.cs.plt.iter.*;
045
046 import junit.framework.TestCase;
047
048
049 /** Used to type check the LHS of an assignment expression such as += or -=, where the left hand side needs to not be final
050 * and already have a value.
051 */
052 public class LValueWithValueTypeChecker extends JExpressionIFAbstractVisitor<TypeData> {
053
054 /** Instance of the testAssignable visitor that will be used to make sure that if the lhs
055 * is something of the type that could be assigned to, it can actually be assigned to*/
056 private final TestAssignable _testAssignableInstance;
057
058 // The visitor that invoked this: holds the error list
059 private final SpecialTypeChecker _bob;
060
061 /* Constructor for LValueTypeChecker. Initializes _testAssignableInstance.
062 * @param bob The visitor that invoked this visitor.
063 */
064 public LValueWithValueTypeChecker(SpecialTypeChecker bob) {
065 _testAssignableInstance = new TestAssignable(bob._data, bob._file, bob._package, bob._importedFiles, bob._importedPackages, bob._vars, bob._thrown);
066 _bob = bob;
067 }
068
069 /** Most expressions cannot appear on the lhs of an assignment: give an appropriate error */
070 public TypeData defaultCase(JExpressionIF that) {
071 _bob._addError("You cannot assign a value to an expression of this kind. Values can only be assigned to fields or variables", that);
072 return null;
073 }
074
075 /**An increment expression is a special case that cannot appear on the lhs*/
076 public TypeData forIncrementExpression(IncrementExpression that) {
077 _bob._addError("You cannot assign a value to an increment expression", that);
078 return null;
079 }
080
081 /* Names can appear on the lhs, so check to see if the name can be assigned to*/
082 public TypeData forNameReference(NameReference that) {
083 return that.visit(_testAssignableInstance);
084 }
085
086 /** Array accesses can appear on the lhs, so check to see if the array can be assigned to*/
087 public TypeData forArrayAccess(ArrayAccess that) {
088 return that.visit(_testAssignableInstance);
089 }
090
091 /** Recur on the value stored in the parentheses*/
092 public TypeData forParenthesized(Parenthesized that) {
093 return that.getValue().visit(this);
094 }
095
096 /** Checks to see if what is on the lhs is assignable to and already has a value*/
097 private class TestAssignable extends ExpressionTypeChecker {
098
099 public TestAssignable(Data data, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
100 super(data, file, packageName, importedFiles, importedPackages, vars, thrown);
101 }
102
103 /** The variable referenced here should already have a value and should not be final. */
104 public TypeData forSimpleNameReference(SimpleNameReference that) {
105 Word myWord = that.getName();
106 myWord.visit(this);
107
108 VariableData reference = getFieldOrVariable(myWord.getText(), _data, _data.getSymbolData(), that, _vars, true, true);
109 if (reference != null) {
110 if (!reference.hasValue()) {
111 _addError("You cannot use " + reference.getName() + " here, because it may not have been given a value", that.getName());
112 }
113 else if (reference.isFinal()) {
114 _addError("You cannot assign a new value to " + reference.getName() + " because it is immutable and has already been given a value", that.getName());
115 }
116
117 //if reference is non-static, but context is static, give error
118 else if (! reference.hasModifier("static") && inStaticMethod()) {
119 _addError("Non static field or variable " + reference.getName() + " cannot be referenced from a static context", that);
120 }
121
122 return reference.getType().getInstanceData();
123
124 }
125
126 SymbolData classR = findClassReference(null, myWord.getText(), that);
127 if (classR == SymbolData.AMBIGUOUS_REFERENCE) {return null;}
128 if (classR != null) {
129 if (checkAccess(that, classR.getMav(), classR.getName(), classR, _data.getSymbolData(), "class or interface", false)) {
130 return classR;
131 }
132 }
133 PackageData packageD = new PackageData(myWord.getText());
134 return packageD;
135 }
136
137 /**
138 * Here is a table that explains what is allowed:
139 * result:
140 * left: package | symbol | instance
141 * package | yes yes(if class exists) no
142 * symbol | no yes, if static inner class yes if field is static and assignable
143 * instance | no ERROR yes, if field is assignable
144 */
145 public TypeData forComplexNameReference(ComplexNameReference that) {
146 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
147 TypeData lhs = that.getEnclosing().visit(etc);
148 _bob.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
149 Word myWord = that.getName();
150
151 //if lhs is a package data, either we found a class reference or this piece is still part of the package
152 if (lhs instanceof PackageData) {
153 SymbolData classRef = findClassReference(lhs, myWord.getText(), that);
154 if (classRef != null) {return classRef;}
155 return new PackageData((PackageData) lhs, myWord.getText());
156 }
157
158 //if the word is a variable reference, make sure it can be seen from this context
159 VariableData reference = getFieldOrVariable(myWord.getText(), lhs.getSymbolData(), _data.getSymbolData(), that);
160 if (reference != null) {
161 if (lhs instanceof SymbolData) {
162 //does this reference a field? if so, it must be static
163 if (!reference.hasModifier("static")) {
164 _addError("Non-static variable " + reference.getName() + " cannot be accessed from the static context " + Data.dollarSignsToDots(lhs.getName()) + ". Perhaps you meant to instantiate an instance of " + Data.dollarSignsToDots(lhs.getName()), that);
165 return reference.getType().getInstanceData();
166 }
167 }
168
169 //make sure it already had a value
170 if (!reference.hasValue()) {
171 _addError("You cannot use " + reference.getName() + " here, because it may not have been given a value", that.getName());
172 }
173
174 //make sure it can be assigned
175 if (!canBeAssigned(reference)) {
176 _addError("You cannot assign a new value to " + reference.getName() + " because it is immutable and has already been given a value", that.getName());
177 }
178 return reference.getType().getInstanceData();
179 }
180
181 //does this reference an inner class? if so, it must be static
182 SymbolData sd = getSymbolData(true, myWord.getText(), lhs.getSymbolData(), that, false);
183 if (sd != null && sd != SymbolData.AMBIGUOUS_REFERENCE) {
184
185 if (!checkAccess(that, sd.getMav(), sd.getName(), sd, _data.getSymbolData(), "class or interface")) {return null;}
186
187
188 if (!sd.hasModifier("static")) {
189 _addError("Non-static inner class " +Data.dollarSignsToDots(sd.getName()) + " cannot be accessed from this context. Perhaps you meant to instantiate it", that);
190 }
191
192 //you cannot reference static inner classes from the context of an instantiation of their outer class
193 else if (lhs instanceof InstanceData) {
194 _addError("You cannot reference the static inner class " + Data.dollarSignsToDots(sd.getName()) + " from an instance of " + Data.dollarSignsToDots(lhs.getName()), that);
195 }
196 return sd;
197 }
198
199 if (sd != SymbolData.AMBIGUOUS_REFERENCE) {_addError("Could not resolve " + myWord.getText() +
200 " from the context of " +
201 Data.dollarSignsToDots(lhs.getName()), that);}
202 return null;
203 }
204
205 /** Type-check the lhs and the index. */
206 public TypeData forArrayAccess(ArrayAccess that) {
207 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
208 TypeData lhs = that.getArray().visit(etc);
209 _bob.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned); //update internal SpecialTypeChecker's list of what got assigned
210
211 ExpressionTypeChecker indexTC = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
212 TypeData index = that.getIndex().visit(indexTC);
213 _bob.thingsThatHaveBeenAssigned.addAll(indexTC.thingsThatHaveBeenAssigned); //update internal SpecialTypeChecker's list of what got assigned
214
215 return forArrayAccessOnly(that, lhs, index);
216 }
217
218 }
219
220
221 /**Test the methods defined in the above class*/
222 public static class LValueWithValueTypeCheckerTest extends TestCase {
223
224 private LValueWithValueTypeChecker _lvtc;
225 LValueWithValueTypeChecker.TestAssignable _ta;
226
227
228 private SymbolData _sd1;
229 private SymbolData _sd2;
230 private SymbolData _sd3;
231 private SymbolData _sd4;
232 private SymbolData _sd5;
233 private SymbolData _sd6;
234 private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
235 private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
236 private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
237 private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
238 private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
239 private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
240 private ModifiersAndVisibility _finalPublicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final", "public"});
241 private ModifiersAndVisibility _publicAbstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "abstract"});
242 private ModifiersAndVisibility _publicStaticMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "static"});
243
244
245 public LValueWithValueTypeCheckerTest() {
246 this("");
247 }
248 public LValueWithValueTypeCheckerTest(String name) {
249 super(name);
250 }
251
252 public void setUp() {
253 _sd1 = new SymbolData("i.like.monkey");
254 _sd2 = new SymbolData("i.like.giraffe");
255 _sd3 = new SymbolData("zebra");
256 _sd4 = new SymbolData("u.like.emu");
257 _sd5 = new SymbolData("");
258 _sd6 = new SymbolData("cebu");
259 _lvtc =
260 new LValueWithValueTypeChecker(new SpecialTypeChecker(_sd1, new File(""), "", new LinkedList<String>(),
261 new LinkedList<String>(), new LinkedList<VariableData>(),
262 new LinkedList<Pair<SymbolData, JExpression>>()));
263 _ta = _lvtc._testAssignableInstance;
264 _lvtc._bob.errors = new LinkedList<Pair<String, JExpressionIF>>();
265 LanguageLevelConverter.symbolTable.clear();
266 // LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, IterUtil.<File>empty());
267 _lvtc._bob._importedPackages.addFirst("java.lang");
268 }
269
270 //Test methods of LeftValueTypeChecker:
271
272 public void testDefaultCase() {
273 //should add an error
274 new NullLiteral(SourceInfo.NONE).visit(_lvtc);
275 assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
276 assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind. Values can only be assigned to fields or variables",
277 _lvtc._bob.errors.getLast().getFirst());
278
279 //should add an error
280 new PlusExpression(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 21), new IntegerLiteral(SourceInfo.NONE, 22)).visit(_lvtc);
281 assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
282 assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind. Values can only be assigned to fields or variables",
283 _lvtc._bob.errors.getLast().getFirst());
284 }
285
286 public void testForIncrementExpression() {
287 //should add an error
288 PositivePrefixIncrementExpression p = new PositivePrefixIncrementExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "bob")));
289 assertEquals("Should return null", null, p.visit(_lvtc));
290 assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
291 assertEquals("Error message should be correct", "You cannot assign a value to an increment expression", _lvtc._bob.errors.getLast().getFirst());
292 }
293
294
295 public void testForSimpleNameReference() {
296 //first, consider the case where what we have is a variable reference:
297 SimpleNameReference var = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "variable1"));
298 VariableData varData = new VariableData("variable1", _publicMav, SymbolData.INT_TYPE, false, _ta._data);
299 _ta._vars.add(varData);
300
301 //in this case, it has not been initialized--should throw error because it needs to have already had a value
302 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
303 assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
304 assertEquals("Error message should be correct", "You cannot use variable1 here, because it may not have been given a value", _lvtc._bob.errors.getLast().getFirst());
305
306 //if it has been initialized but is not final, do not give an error
307 varData.gotValue();
308 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
309 assertEquals("Should still be 1 error", 1, _lvtc._bob.errors.size());
310
311 //if it has been initialized and is final, give an error
312 varData.setMav(_finalMav);
313 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
314 assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
315 assertEquals("Error message should be correct", "You cannot assign a new value to variable1 because it is immutable and has already been given a value", _lvtc._bob.errors.getLast().getFirst());
316 varData.setMav(_publicMav);
317
318 //if variable is non-static, but you are in static context, cannot reference it. Should give error
319 MethodData newContext = new MethodData("method", _publicStaticMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[0], new String[0], _sd1, new NullLiteral(SourceInfo.NONE));
320 _ta._data = newContext;
321 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
322 assertEquals("Should be 3 errors", 3, _lvtc._bob.errors.size());
323 assertEquals("Error message should be correct", "Non static field or variable variable1 cannot be referenced from a static context", _lvtc._bob.errors.getLast().getFirst());
324 _ta._data = _sd1;
325
326 //if it is a variable of your super class, it won't be in _vars. Check this case.
327 _ta._vars = new LinkedList<VariableData>();
328 _sd1.setSuperClass(_sd2);
329 _sd2.addVar(varData);
330 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
331 assertEquals("Should still be 3 errors", 3, _lvtc._bob.errors.size());
332
333 //now, consider the case where what we have is a class reference:
334 SimpleNameReference className = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Frog"));
335 SymbolData frog = new SymbolData("Frog");
336 frog.setIsContinuation(false);
337 _lvtc._bob.symbolTable.put("Frog", frog);
338
339 //if it is not visibile from this context, return package data
340 TypeData result = className.visit(_lvtc);
341 assertTrue("Result should be a PackageData since Frog is not accessible", result instanceof PackageData);
342 assertEquals("Should have correct name", "Frog", result.getName());
343 assertEquals("Should still be 3 errors", 3, _lvtc._bob.errors.size());
344
345 //if it is visibile from this context, no error
346 frog.setMav(_publicMav);
347 assertEquals("Should return Frog", frog, className.visit(_lvtc));
348 assertEquals("Should still be 3 errors", 3, _lvtc._bob.errors.size());
349
350 //If the name cannot be resolved, simply return a packageData.
351 SimpleNameReference fake = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "notRealReference"));
352 assertEquals("Should return package data", "notRealReference", (fake.visit(_lvtc)).getName());
353 assertEquals("Should still be just 3 errors", 3, _lvtc._bob.errors.size());
354
355 //if the reference is ambiguous (matches both an interface and a class) give an error
356 SimpleNameReference ambigRef = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "ambigThing"));
357
358 SymbolData interfaceD = new SymbolData("interface");
359 interfaceD.setIsContinuation(false);
360 interfaceD.setInterface(true);
361 interfaceD.setMav(_publicMav);
362
363 SymbolData classD = new SymbolData("superClass");
364 classD.setIsContinuation(false);
365 classD.setMav(_publicMav);
366
367 SymbolData ambigThingI = new SymbolData("ambigThing");
368 ambigThingI.setIsContinuation(false);
369 ambigThingI.setInterface(true);
370 interfaceD.addInnerInterface(ambigThingI);
371 ambigThingI.setOuterData(interfaceD);
372 ambigThingI.setMav(_publicStaticMav);
373
374 SymbolData ambigThingC = new SymbolData("ambigThing");
375 ambigThingC.setIsContinuation(false);
376 classD.addInnerClass(ambigThingC);
377 ambigThingC.setOuterData(classD);
378 ambigThingC.setMav(_publicStaticMav);
379
380 _sd6.addInterface(interfaceD);
381 _sd6.setSuperClass(classD);
382
383 _sd6.setMav(_publicMav);
384 _sd6.setIsContinuation(false);
385
386 _ta._data = _sd6;
387
388 assertEquals("Should return null", null, ambigRef.visit(_lvtc));
389 assertEquals("Should be 4 errors", 4, _lvtc._bob.errors.size());
390 assertEquals("Error message should be correct", "Ambiguous reference to class or interface ambigThing",
391 _lvtc._bob.errors.getLast().getFirst());
392 }
393
394
395 public void testForComplexNameReference() {
396 //if lhs is a package data, we want to keep building it:
397
398 //if whole reference is just package reference, return package data
399 ComplexNameReference ref1 = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "java")), new Word(SourceInfo.NONE, "lang"));
400 assertEquals("Should return correct package data", "java.lang", ref1.visit(_lvtc).getName());
401 assertEquals("Should be no errors", 0, _lvtc._bob.errors.size());
402
403 //if reference builds to a class in the symbol table, return that class
404 ComplexNameReference ref2 = new ComplexNameReference(SourceInfo.NONE, ref1, new Word(SourceInfo.NONE, "String"));
405 SymbolData string = new SymbolData("java.lang.String");
406 string.setPackage("java.lang");
407 string.setMav(_publicMav);
408 string.setIsContinuation(false);
409 _lvtc._bob.symbolTable.put("java.lang.String", string);
410
411 assertEquals("Should return string", string, ref2.visit(_lvtc));
412
413 assertEquals("Should still be no errors", 0, _lvtc._bob.errors.size());
414
415
416 //if lhs is not a package data, it gets more complicated:
417
418 //we're referencing a variable inside of symbol data lhs:
419 VariableData myVar = new VariableData("myVar", _publicStaticMav, SymbolData.DOUBLE_TYPE, true, string);
420 string.addVar(myVar);
421 ComplexNameReference varRef1 = new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "myVar"));
422
423 //static var from static context
424 assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
425 assertEquals("There should still be no errors", 0, _lvtc._bob.errors.size());
426
427 //static uninitialized var from static context--give error
428 myVar.lostValue();
429 assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
430 assertEquals("There should be one error", 1, _lvtc._bob.errors.size());
431 assertEquals("Error message should be correct", "You cannot use myVar here, because it may not have been given a value", _lvtc._bob.errors.getLast().getFirst());
432
433
434 //non-static var--this is a static context
435 myVar.setMav(_publicMav);
436 assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
437 assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
438 assertEquals("Error message should be correct", "Non-static variable myVar cannot be accessed from the static context java.lang.String. Perhaps you meant to instantiate an instance of java.lang.String", _lvtc._bob.errors.getLast().getFirst());
439
440
441 //non-static context, okay to reference non-static var
442 VariableData stringVar = new VariableData("s", _publicMav, string, true, _lvtc._bob._data);
443 _ta._vars.add(stringVar);
444 myVar.gotValue();
445 ComplexNameReference varRef2 = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s")), new Word(SourceInfo.NONE, "myVar"));
446 assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
447 assertEquals("Should still just be 2 errors", 2, _lvtc._bob.errors.size());
448
449 //if it has been initialized and is final, give an error
450 myVar.setMav(_finalPublicMav);
451 myVar.gotValue();
452 assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
453 assertEquals("Should be 3 errors", 3, _lvtc._bob.errors.size());
454 assertEquals("Error message should be correct", "You cannot assign a new value to myVar because it is immutable and has already been given a value", _lvtc._bob.errors.getLast().getFirst());
455 myVar.setMav(_publicMav);
456
457 //if it is a variable of the super class, you should still be able to see it. Check this case.
458 myVar.setMav(_publicMav);
459 string.setVars(new LinkedList<VariableData>());
460 string.setSuperClass(_sd2);
461 _sd2.addVar(myVar);
462 assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
463 assertEquals("Should still be 3 errors", 3, _lvtc._bob.errors.size());
464
465 //here's a complex multiple variable reference case:
466 VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, true, _sd1);
467 VariableData vd2 = new VariableData("Santa's Little Helper", _publicMav, _sd1, true, _sd2);
468 VariableData vd3 = new VariableData("Snowball1", _publicMav, _sd2, true, _sd3);
469 _sd3.addVar(vd3);
470 _sd2.addVar(vd2);
471 _sd1.addVar(vd1);
472
473 ComplexNameReference varRef3 = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "Snowball1")),
474 new Word(SourceInfo.NONE, "Santa's Little Helper"));
475 ComplexNameReference varRef4 = new ComplexNameReference(SourceInfo.NONE, varRef3, new Word(SourceInfo.NONE, "Mojo"));
476
477 Data oldData = _lvtc._bob._data;
478 _lvtc._bob._data = _sd3;
479 _lvtc._bob._vars.add(vd3);
480
481 TypeData result = varRef4.visit(_lvtc);
482 assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), result);
483 assertEquals("Should still be 3 errors", 3, _lvtc._bob.errors.size());
484
485
486 _lvtc._bob._data = oldData;
487
488
489 //what if what we have is an inner class?
490 SymbolData inner = new SymbolData("java.lang.String$Inner");
491 inner.setPackage("java.lang");
492 inner.setIsContinuation(false);
493 inner.setOuterData(string);
494 string.addInnerClass(inner);
495
496
497 //if inner is not visible, throw error
498 ComplexNameReference innerRef0 = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s")), new Word(SourceInfo.NONE, "Inner"));
499 assertEquals("Should return null", null, innerRef0.visit(_lvtc));
500 assertEquals("Should be 4 errors", 4, _lvtc._bob.errors.size());
501 assertEquals("Error message should be correct", "The class or interface java.lang.String.Inner is package protected because there is no access specifier and cannot be accessed from i.like.monkey", _lvtc._bob.errors.getLast().getFirst());
502
503 inner.setMav(_publicMav);
504
505 //if inner is not static, give error:
506 ComplexNameReference innerRef1 = new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "Inner"));
507 assertEquals("Should return inner", inner, innerRef1.visit(_lvtc));
508 assertEquals("Should be 5 errors", 5, _lvtc._bob.errors.size());
509 assertEquals("Error message should be correct", "Non-static inner class java.lang.String.Inner cannot be accessed from this context. Perhaps you meant to instantiate it", _lvtc._bob.errors.getLast().getFirst());
510
511 //if inner is not static and outer is not static, it's okay...
512 ComplexNameReference innerRef2 = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "s")), new Word(SourceInfo.NONE, "Inner"));
513 assertEquals("Should return inner", inner, innerRef2.visit(_lvtc));
514 assertEquals("Should be 6 errors", 6, _lvtc._bob.errors.size());
515 assertEquals("Error message should be correct", "Non-static inner class java.lang.String.Inner cannot be accessed from this context. Perhaps you meant to instantiate it", _lvtc._bob.errors.getLast().getFirst());
516
517 //if inner is static and outer is not static, throw error
518 inner.setMav(_publicStaticMav);
519 assertEquals("Should return inner", inner, innerRef2.visit(_lvtc));
520 assertEquals("Should be 7 errors", 7, _lvtc._bob.errors.size());
521 assertEquals("Error message should be correct", "You cannot reference the static inner class java.lang.String.Inner from an instance of java.lang.String", _lvtc._bob.errors.getLast().getFirst());
522
523
524 //if the symbol could not be matched, give an error and return null
525 ComplexNameReference noSense = new ComplexNameReference(SourceInfo.NONE, ref2, new Word(SourceInfo.NONE, "nonsense"));
526 assertEquals("Should return null", null, noSense.visit(_lvtc));
527 assertEquals("Should be 8 errors", 8, _lvtc._bob.errors.size());
528 assertEquals("Error message should be correct", "Could not resolve nonsense from the context of java.lang.String", _lvtc._bob.errors.getLast().getFirst());
529
530 //if the reference is ambiguous (matches both an interface and a class) give an error
531 ComplexNameReference ambigRef = new ComplexNameReference(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "cebu")), new Word(SourceInfo.NONE, "ambigThing"));
532
533 SymbolData interfaceD = new SymbolData("interface");
534 interfaceD.setIsContinuation(false);
535 interfaceD.setInterface(true);
536 interfaceD.setMav(_publicMav);
537
538 SymbolData classD = new SymbolData("superClass");
539 classD.setIsContinuation(false);
540 classD.setMav(_publicMav);
541
542 SymbolData ambigThingI = new SymbolData("ambigThing");
543 ambigThingI.setIsContinuation(false);
544 ambigThingI.setInterface(true);
545 interfaceD.addInnerInterface(ambigThingI);
546 ambigThingI.setOuterData(interfaceD);
547 ambigThingI.setMav(_publicStaticMav);
548
549 SymbolData ambigThingC = new SymbolData("ambigThing");
550 ambigThingC.setIsContinuation(false);
551 classD.addInnerClass(ambigThingC);
552 ambigThingC.setOuterData(classD);
553 ambigThingC.setMav(_publicStaticMav);
554
555 _sd6.addInterface(interfaceD);
556 _sd6.setSuperClass(classD);
557
558 _lvtc._bob.symbolTable.put("cebu", _sd6);
559 _sd6.setMav(_publicMav);
560 _sd6.setIsContinuation(false);
561
562 assertEquals("Should return null", null, ambigRef.visit(_lvtc));
563 assertEquals("Should be 9 errors", 9, _lvtc._bob.errors.size());
564 // TODO: should following error message mention the context 'cebu'?
565 assertEquals("Error message should be correct", "Ambiguous reference to class or interface ambigThing",
566 _lvtc._bob.errors.getLast().getFirst());
567
568 }
569
570
571 public void testForArrayAccess() {
572 ArrayData intArray =
573 new ArrayData(SymbolData.INT_TYPE,
574 new LanguageLevelVisitor(_lvtc._bob._file,
575 _lvtc._bob._package,
576 null, // enclosingClassName for top level traversal
577 _lvtc._bob._importedFiles,
578 _lvtc._bob._importedPackages,
579 new HashSet<String>(),
580 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
581 new LinkedList<Command>()),
582 SourceInfo.NONE);
583 VariableData variable1 = new VariableData("variable1", _publicMav, intArray, true, _ta._data);
584 _ta._vars.add(variable1);
585
586 VariableData intVar = new VariableData("intVar", _publicMav, SymbolData.INT_TYPE, true, _ta._data);
587 _ta._vars.add(intVar);
588
589 MethodData makeArray = new MethodData("makeArray", _privateMav, new TypeParameter[0], intArray, new VariableData[0], new String[0], _ta._data.getSymbolData(), new NullLiteral(SourceInfo.NONE));
590 _ta._data.getSymbolData().addMethod(makeArray);
591
592 //first, a simple index into an int[]
593
594 ArrayAccess a1 = new ArrayAccess(SourceInfo.NONE,
595 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "variable1")),
596 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "intVar")));
597
598 assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a1.visit(_lvtc));
599 assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
600
601
602 //make sure that an arbitrary expression can occur in the index
603
604 ArrayAccess a2 = new ArrayAccess(SourceInfo.NONE,
605 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "variable1")),
606 new PlusExpression(SourceInfo.NONE,
607 new IntegerLiteral(SourceInfo.NONE, 12),
608 new IntegerLiteral(SourceInfo.NONE, 22)));
609
610 assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a2.visit(_lvtc));
611 assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
612
613 //make sure that an arbitrary expression can occur in the array
614
615 ArrayAccess a3 = new ArrayAccess(SourceInfo.NONE,
616 new SimpleMethodInvocation(SourceInfo.NONE,
617 new Word(SourceInfo.NONE, "makeArray"),
618 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])),
619 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "intVar")));
620 assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a3.visit(_lvtc));
621 assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
622 }
623
624 public void testForParenthesized() {
625
626 VariableData x = new VariableData("x", _publicMav, SymbolData.INT_TYPE, true, _ta._data);
627 _ta._vars.add(x);
628
629 // make sure (((x))) is okay
630 Parenthesized p1 = new Parenthesized(SourceInfo.NONE,
631 new Parenthesized(SourceInfo.NONE,
632 new Parenthesized(SourceInfo.NONE,
633 new SimpleNameReference(SourceInfo.NONE,
634 new Word(SourceInfo.NONE, "x")))));
635
636 assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), p1.visit(_lvtc));
637 assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
638
639 // make sure ((1)) breaks
640 Parenthesized p2 = new Parenthesized(SourceInfo.NONE,
641 new Parenthesized(SourceInfo.NONE,
642 new IntegerLiteral(SourceInfo.NONE, 1)));
643 assertEquals("should return null", null, p2.visit(_lvtc));
644 assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
645 assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind. Values can only be assigned to fields or variables",
646 _lvtc._bob.errors.getLast().getFirst());
647
648 }
649
650
651
652 //methods of TestAssignable are implicitly tested above.
653
654 }
655 }