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