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    }