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    }