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.*;
043    import edu.rice.cs.plt.reflect.JavaVersion;
044    import edu.rice.cs.plt.iter.*;
045    
046    import junit.framework.TestCase;
047    
048    /** Do the TypeChecking appropriate to the context of a class body.  Common to all Language Levels. */
049    public class ClassBodyTypeChecker extends SpecialTypeChecker {
050      
051      /**The SymbolData corresponding to this class.*/
052      private SymbolData _symbolData;
053      
054      /**True if we encounter a ConstructorDef while visiting the body.*/
055      protected boolean hasConstructor;
056      
057      /** Constructor for ClassBodyTypeChecker. Adds all the variables in the symbol data to the list of what can be seen
058        * from this context, since class fields can always be seen.
059        * @param sd  The SymbolData of the class we are type checking.
060        * @param file  The File corresponding to the source file we are checking.
061        * @param packageName  The package of the source file.
062        * @param importedFiles  A list of the names of the classes that are specifically imported in the source file
063        * @param importedPackages  A list of the names of the packages that are imported in the source file.
064        * @param vars  A list of the variable datas that can be seen and have been given a value before this context
065        * @param thrown  The exceptions that are thrown
066        */
067      public ClassBodyTypeChecker(SymbolData sd, File file, String packageName, LinkedList<String> importedFiles, 
068                                  LinkedList<String> importedPackages, LinkedList<VariableData> vars, 
069                                  LinkedList<Pair<SymbolData, JExpression>> thrown) {
070        super(sd, file, packageName, importedFiles, importedPackages, vars, thrown);
071        if (sd == null) throw new RuntimeException("SymbolData is null in new ClassBodyTypeChecker operation");
072        _symbolData = sd;
073        hasConstructor = false;
074        assert _vars == vars;
075        
076        LinkedList<VariableData> classVars = sd.getVars();
077        
078    //    for (VariableData vd: classVars) {
079    //      if (vd.isFinal() && vd.gotValue()) thingsThatHaveBeenAssigned.addLast(vd);
080    //    }
081        
082        _vars.addAll(classVars);
083        
084        LinkedList<VariableData> superVars = sd.getAllSuperVars();
085        for (VariableData vd: superVars) {
086          if (vd.isFinal() && vd.gotValue()) thingsThatHaveBeenAssigned.addLast(vd);
087        }
088        _vars.addAll(superVars);
089      }
090      
091      /** @return the symbol data corresponding to the class we are visiting*/
092      protected Data _getData() { return _symbolData; }
093      
094      /** We need to do this so that expressions (which should only occur in variable initializers and
095        * initializer blocks) can know which fields have already been declared
096        */
097      public TypeData forUninitializedVariableDeclaratorOnly(UninitializedVariableDeclarator that, 
098                                                             TypeData typeRes, 
099                                                             TypeData nameRes) {
100        Word name = that.getName();
101        String text = that.getName().getText();
102        VariableData vd = getFieldOrVariable(text, _symbolData, _symbolData, name);
103        if (vd == null) {
104          throw new RuntimeException("The field " + text + " was not found in " + _symbolData.getName() + '.');
105        }
106        _vars.addLast(vd);
107        return null;
108      }
109    
110      /** Make sure the method is not missing a return type.  
111       * If expected and actual are both not null, then their relationship (i.e.
112       * is actual a subclass of expected) is checked when the value return statement that returns
113       * actual is processed.
114       */
115      private void _checkReturnType(SymbolData expected, SymbolData actual, ConcreteMethodDef that) {
116        // If the return type is void, the BodyTypeChecker will make sure there are no return statements with expressions.
117        if (expected == SymbolData.VOID_TYPE) {
118          if (actual == null || actual == SymbolData.VOID_TYPE) {
119            // correct, do nothing
120          }
121        }
122        else {
123          if (actual == null) {
124            _addError("This method is missing a return statement.", that);
125          }
126        }
127      }
128      
129      /** Finds the corresponding MethodData for this constructor first.  Then visits the body, making sure that
130        * all final fields are assigned a value and that no final fields are reassigned.
131        */
132      public TypeData forConstructorDef(ConstructorDef that) {
133        hasConstructor = true;
134        final TypeData mavRes = that.getMav().visit(this);
135        final TypeData[] parameters_result = makeArrayOfRetType(that.getParameters().length);
136        final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
137        for (int i = 0; i < that.getThrows().length; i++) {
138          throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that);     //that.getThrows()[i].visit(this);
139        }
140    
141        // We need to match the name and params.
142        // First find the correct MethodData.
143        MethodData md = null;
144        FormalParameter[] fParams = that.getParameters();
145        String[] paramTypes = new String[fParams.length];
146        for (int i = 0; i < fParams.length; i++) {
147          paramTypes[i] = fParams[i].getDeclarator().getType().getName();
148        }
149        LinkedList<MethodData> mds = _symbolData.getMethods();
150        Iterator<MethodData> iter = mds.iterator();
151        while (iter.hasNext()) {
152          boolean match = true;
153          MethodData tempMd = iter.next();
154          if (tempMd.getName().equals(LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()))) {
155            // Check the params.
156            VariableData[] vds = tempMd.getParams();
157            if (paramTypes.length == vds.length) {
158              for (int i = 0; i < paramTypes.length; i++) {
159                // The parameters should be in order.  Must also check the unqualified form of the VariableData's type.
160                if(!vds[i].getType().getName().equals(paramTypes[i]) &&
161                   !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
162                  match = false;
163                  break;
164                }
165              }
166              if (match) {
167                md = tempMd;            
168                break;
169              }
170            }
171          }
172        }
173        if (md == null) { throw
174          new RuntimeException("The constructor " + LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()) + 
175                               " was not in the class " + _symbolData.getName() + '.');
176        }
177    
178        LinkedList<VariableData> ll = new LinkedList<VariableData>();// = cloneVariableDataList(_vars);
179        VariableData[] vds = md.getParams();
180        for (int i = 0; i<vds.length; i++) {
181          ll.addLast(vds[i]);
182        }
183        ll.addAll(cloneVariableDataList(_vars));
184    
185        ConstructorBodyTypeChecker btc = new ConstructorBodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll, new LinkedList<Pair<SymbolData, JExpression>>());
186        final TypeData bodyRes = that.getStatements().visit(btc);
187        
188        //make sure the constructor assigns a value to any uninitialized fields (i.e. final fields)
189        LinkedList<VariableData> sdVars = _symbolData.getVars();
190        for (int i = 0; i < sdVars.size(); i++) { // Non-finals automatically get values, so only need to check finals
191          if (! sdVars.get(i).hasValue()) {
192            _addError("The final field " + sdVars.get(i).getName() + " has not been initialized.  Make sure you give it a value in this constructor", that);
193            return null;
194          }
195        }
196        
197        _symbolData.decrementConstructorCount();
198        
199       
200        if (_symbolData.getConstructorCount() > 0) {
201          unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
202        }
203        
204        return forConstructorDefOnly(that, mavRes, parameters_result, throwsRes, bodyRes);
205      }
206      
207    
208      /*Basically, a no-op*/
209      public TypeData forConstructorDefOnly(ConstructorDef that, TypeData mavRes, TypeData[] parameters_result, TypeData[] throwsRes, TypeData bodyRes) {
210        return forJExpressionOnly(that);
211      }
212      
213      /** Visit all of the fields of the ConcreteMethodDef, and resolve everything.  Then, find
214        * the corresponding MethodData in the SymbolData's list of methods.  It must match both in name and parameter
215        * types.  Keep track of what has been assigned before we visit the method body, and then visit the
216        * method body.  Then, make sure the return type of the method is okay, and unassign any variables
217        * for which the assignment should not be visible outside the scope of the method.
218        */
219      public TypeData forConcreteMethodDef(ConcreteMethodDef that) {
220        final TypeData mavRes = that.getMav().visit(this);
221        final TypeData[] typeParamsRes = makeArrayOfRetType(that.getTypeParams().length);
222        for (int i = 0; i < that.getTypeParams().length; i++) {
223          typeParamsRes[i] = that.getTypeParams()[i].visit(this);
224        }
225        final SymbolData resRes = getSymbolData(that.getResult().getName(), _symbolData, that);
226        final TypeData nameRes = that.getName().visit(this);
227        final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
228        for (int i = 0; i < that.getThrows().length; i++) {
229          throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
230        }
231        // We need to match the name and params.
232        // First find the correct MethodData.
233        MethodData md = null;
234        FormalParameter[] fParams = that.getParams();
235        String[] paramTypes = new String[fParams.length];
236        for (int i = 0; i < fParams.length; i++) {
237          paramTypes[i] = fParams[i].getDeclarator().getType().getName();
238        }
239        LinkedList<MethodData> mds = _symbolData.getMethods();
240        Iterator<MethodData> iter = mds.iterator();
241        while (iter.hasNext()) {
242          boolean match = false;
243          MethodData tempMd = iter.next();
244          if (tempMd.getName().equals(that.getName().getText())) {
245            match = true;
246    
247            // Check the params.
248            VariableData[] vds = tempMd.getParams();
249            if (paramTypes.length == vds.length) {
250              for (int i = 0; i < paramTypes.length; i++) {
251                // The parameters should be in order.  Must also check the unqualified form of the VariableData's type.
252                if(!vds[i].getType().getName().equals(paramTypes[i]) &&
253                   !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
254                  match = false;
255                  break;
256                }
257              }
258              if (match) {
259                md = tempMd;            
260                break;
261              }
262            }
263          }
264        }
265        if (md == null) {
266          throw new RuntimeException("Internal Program Error: The method " + that.getName().getText() + " was not in the class " + _symbolData.getName() + ".  Please report this bug.");
267        }
268        
269        LinkedList<VariableData> ll = new LinkedList<VariableData>();
270        VariableData[] vds = md.getParams();
271        for (int i = 0; i < vds.length; i++) {
272          ll.addLast(vds[i]);
273        }
274        ll.addAll(cloneVariableDataList(_vars));
275        
276        LinkedList<VariableData> thingsWeAssigned = new LinkedList<VariableData>();
277        for (int i = 0; i<_symbolData.getVars().size(); i++) {
278          VariableData tempVd = _symbolData.getVars().get(i);
279          if (tempVd.gotValue()) { //then this variable did not have a value previously.
280            thingsWeAssigned.addLast(tempVd);
281          }
282        }
283        
284        BodyTypeChecker btc = new BodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll, 
285                                                  new LinkedList<Pair<SymbolData, JExpression>>());
286        
287        TypeData bodyRes = that.getBody().visit(btc); // We assume that this will return an InstanceData -- the return type of the body
288        
289        // This checks to see that the method returns the correct type.  It throws its own errors.
290        if (bodyRes != null) {bodyRes = bodyRes.getSymbolData();}
291        _checkReturnType(md.getReturnType(), (SymbolData) bodyRes, that);
292        if (md.getReturnType() != null) {
293          // Ensure that this method doesn't override another method with a different return type.
294          SymbolData.checkDifferentReturnTypes(md, _symbolData, LanguageLevelConverter.OPT.javaVersion());
295        }
296        
297        // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
298        final TypeData[] paramsRes = makeArrayOfRetType(that.getParams().length);
299    //
300    //    // Why oh why is this necessary; these variables aren't even in scope elsewhere; they should be invisible!!!!
301    //    for (int i = 0; i < thingsWeAssigned.size(); i++) {
302    //      thingsWeAssigned.get(i).lostValue();
303    //    }
304        return resRes;
305      }
306      
307      /*
308       * Make sure that this method does not override another method with a different return type,
309       * since this is not allowed in java.
310       */
311      public TypeData forAbstractMethodDef(AbstractMethodDef that) {
312        final TypeData mavRes = that.getMav().visit(this);
313        final TypeData[] typeParamsRes = makeArrayOfRetType(that.getTypeParams().length);
314        for (int i = 0; i < that.getTypeParams().length; i++) {
315          typeParamsRes[i] = that.getTypeParams()[i].visit(this);
316        }
317        final SymbolData resRes = getSymbolData(that.getResult().getName(), _symbolData, that);
318        final TypeData nameRes = that.getName().visit(this);
319    
320        // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
321        final TypeData[] paramsRes = makeArrayOfRetType(that.getParams().length);
322        for (int i = 0; i<paramsRes.length; i++) {
323          paramsRes[i] = getSymbolData(that.getParams()[i].getDeclarator().getType().getName(), _symbolData, that.getParams()[i]);
324        }
325        final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
326        for (int i = 0; i < that.getThrows().length; i++) {
327          throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
328        }
329        // Ensure that this method doesn't override another method with a different return type.
330        MethodData md = _symbolData.getMethod(that.getName().getText(), paramsRes);
331        if (md == null) {
332          throw new RuntimeException("Internal Program Error: Could not find the method " + that.getName().getText() + " in class " + _symbolData.getName() +".  Please report this bug.");
333        }
334        SymbolData.checkDifferentReturnTypes(md, _symbolData, LanguageLevelConverter.OPT.javaVersion());
335    
336        return resRes;
337      }
338      
339      /*Try to resolve the type, and then make sure it can be seen from where we are. */
340      public TypeData forTypeOnly(Type that) {
341        Data sd = getSymbolData(that.getName(), _symbolData, that);
342        if (sd != null) {sd = sd.getOuterData();}
343        while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
344          if (!checkAccess(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _symbolData, "class or interface")) {
345            return null;
346          }
347          sd = sd.getOuterData();
348        }
349        return forJExpressionOnly(that);
350      }
351      
352       /**
353        * Test the methods that are defined above.
354        */
355      public static class ClassBodyTypeCheckerTest extends TestCase {
356        
357        private ClassBodyTypeChecker _cbbtc;
358        
359        private SymbolData _sd1;
360        private SymbolData _sd2;
361        private SymbolData _sd3;
362        private SymbolData _sd4;
363        private SymbolData _sd5;
364        private SymbolData _sd6;
365        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
366        private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
367        private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
368        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
369        private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
370        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
371        
372        
373        public ClassBodyTypeCheckerTest() {
374          this("");
375        }
376        public ClassBodyTypeCheckerTest(String name) {
377          super(name);
378        }
379        
380        public void setUp() {
381          _sd1 = new SymbolData("i.like.monkey");
382          _sd2 = new SymbolData("i.like.giraffe");
383          _sd3 = new SymbolData("zebra");
384          _sd4 = new SymbolData("u.like.emu");
385          _sd5 = new SymbolData("");
386          _sd6 = new SymbolData("cebu");
387          
388          errors = new LinkedList<Pair<String, JExpressionIF>>();
389          LanguageLevelConverter.symbolTable.clear();
390          LanguageLevelConverter._newSDs.clear();
391          _cbbtc = 
392            new ClassBodyTypeChecker(_sd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), 
393                                     new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
394          LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
395          _cbbtc._importedPackages.addFirst("java.lang");
396        }
397        
398        public void testForUninitializedVariableDeclaratorOnly() {
399          VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
400          _sd1.addVar(vd1);
401          UninitializedVariableDeclarator uvd = 
402            new UninitializedVariableDeclarator(SourceInfo.NONE, 
403                                                new PrimitiveType(SourceInfo.NONE, "int"), 
404                                                new Word(SourceInfo.NONE, "Mojo"));
405    //      uvd.visit(_cbbtc);
406          _cbbtc.forUninitializedVariableDeclaratorOnly(uvd, SymbolData.INT_TYPE, null);
407          assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));      
408        }
409        
410        public void testForInitializedVariableDeclaratorOnly() {
411          VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
412          _sd1.addVar(vd1);
413          InitializedVariableDeclarator ivd = new InitializedVariableDeclarator(SourceInfo.NONE, 
414                                                                                new PrimitiveType(SourceInfo.NONE, "int"), 
415                                                                                new Word(SourceInfo.NONE, "Mojo"), 
416                                                                                new IntegerLiteral(SourceInfo.NONE, 1));
417          ivd.visit(_cbbtc);
418          assertEquals("There should be no errors.", 0, errors.size());
419          assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));
420          ivd = new InitializedVariableDeclarator(SourceInfo.NONE, 
421                                                  new PrimitiveType(SourceInfo.NONE, "int"), 
422                                                  new Word(SourceInfo.NONE, "Santa's Little Helper"), 
423                                                  new IntegerLiteral(SourceInfo.NONE, 1));
424          try {
425            ivd.visit(_cbbtc);
426            fail("Should have thrown a RuntimeException because there's no field named Santa's Little Helper.");
427          }
428          catch (RuntimeException re) {
429            assertEquals("The error message should be correct.", "Internal Program Error: The field or variable Santa's Little Helper was not found in this block.  Please report this bug.", re.getMessage());
430          }
431        }
432        
433        public void test_checkReturnType() {
434          /* Check tests that should work correctly first. */
435          _cbbtc._checkReturnType(SymbolData.VOID_TYPE, null, null);
436          _cbbtc._checkReturnType(SymbolData.VOID_TYPE, SymbolData.VOID_TYPE, null);
437          _cbbtc._checkReturnType(SymbolData.INT_TYPE, SymbolData.INT_TYPE, null);
438          _cbbtc._checkReturnType(SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE, null);
439          
440          assertEquals("There should be no errors.", 0, errors.size());
441          
442          /* Check tests that should each throw an error. */
443          _cbbtc._checkReturnType(SymbolData.INT_TYPE, null, null);
444          assertEquals("There should now be 1 error", 1, errors.size());
445          assertEquals("The error message should be correct", "This method is missing a return statement.", errors.get(0).getFirst());
446          
447        }
448        
449        public void testForConcreteMethodDef() {
450          FormalParameter[] fps = new FormalParameter[] {
451            new FormalParameter(SourceInfo.NONE, 
452                                new UninitializedVariableDeclarator(SourceInfo.NONE, 
453                                                                  new PrimitiveType(SourceInfo.NONE, "double"), 
454                                                                  new Word (SourceInfo.NONE, "field1")),
455                                false),
456            new FormalParameter(SourceInfo.NONE, 
457                                new UninitializedVariableDeclarator(SourceInfo.NONE, 
458                                                                  new PrimitiveType(SourceInfo.NONE, "boolean"), 
459                                                                  new Word (SourceInfo.NONE, "field2")),
460                                false)};
461          ConcreteMethodDef cmd = new ConcreteMethodDef(SourceInfo.NONE, 
462                                                        _packageMav, 
463                                                        new TypeParameter[0], 
464                                                        new PrimitiveType(SourceInfo.NONE, "int"), 
465                                                        new Word(SourceInfo.NONE, "methodName"),
466                                                        fps,
467                                                        new ReferenceType[0], 
468                                                        new BracedBody(SourceInfo.NONE, new BodyItemI[] {
469            new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5) )}));
470          MethodData md = new MethodData("methodName", 
471                                         _packageMav, 
472                                         new TypeParameter[0], 
473                                         SymbolData.INT_TYPE, 
474                                         new VariableData[] { new VariableData(SymbolData.DOUBLE_TYPE), new VariableData(SymbolData.BOOLEAN_TYPE) },
475                                         new String[0],
476                                         _sd1,
477                                         null); // no SourceInfo
478          _sd1.addMethod(md);
479          cmd.visit(_cbbtc);
480          assertEquals("There should be no errors.", 0, errors.size());
481          
482          cmd = new ConcreteMethodDef(SourceInfo.NONE, 
483                                                        _packageMav, 
484                                                        new TypeParameter[0], 
485                                                        new PrimitiveType(SourceInfo.NONE, "int"), 
486                                                        new Word(SourceInfo.NONE, "Selma"),
487                                                        fps,
488                                                        new ReferenceType[0], 
489                                                        new BracedBody(SourceInfo.NONE, new BodyItemI[] {
490                                                              new ValueReturnStatement(SourceInfo.NONE, 
491                                                                                       new IntegerLiteral(SourceInfo.NONE, 5))}));
492          
493          try {
494            cmd.visit(_cbbtc);
495            fail("Should have thrown a RuntimeException because there's no method named Selma.");
496          }
497          catch (RuntimeException re) {
498            assertEquals("The error message should be correct.", "Internal Program Error: The method Selma was not in the class i.like.monkey.  Please report this bug.", re.getMessage());
499          }
500          
501          
502          //Check that an uninitialized variable is caught:
503          PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
504          UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
505    
506          Statement s = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
507          ConcreteMethodDef cmd0 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], intt,
508                                                        new Word(SourceInfo.NONE, "invalidMethod"), new FormalParameter[0],
509                                                        new ReferenceType[0], new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), s}));
510          VariableData vd = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
511          MethodData md0 = new MethodData("invalidMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
512                                         new VariableData[0], new String[0], _sd1, cmd0);
513          _sd1.addMethod(md0);
514          vd.setEnclosingData(md0);
515          md0.addVar(vd);
516          
517          _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
518          cmd0.visit(_cbbtc);
519          assertEquals("There should be 1 error", 1, errors.size());
520          assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", errors.get(0).getFirst());
521    
522    
523          
524          //Check that the lexical scope of an if then statement is handled correctly.
525          Expression te = new LessThanExpression(SourceInfo.NONE,
526                                                 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
527                                                 new IntegerLiteral(SourceInfo.NONE, 5));
528          Statement ts = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
529          IfThenStatement ift = new IfThenStatement(SourceInfo.NONE, te, ts);
530          
531          
532          FormalParameter param = new FormalParameter(SourceInfo.NONE, new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
533          BracedBody bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift,
534            new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")))});
535          
536          ConcreteMethodDef cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], 
537                                       intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param}, 
538                                                         new ReferenceType[0], bb);
539    
540          VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
541          VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
542          MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
543                                         new VariableData[] {vd1}, new String[0], _sd1, cmd1);
544          _sd1.addMethod(md1);
545          vd1.setEnclosingData(md1);
546          vd2.setEnclosingData(md1);
547          md1.addVar(vd1);
548          md1.addVar(vd2);
549          
550          _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
551          cmd1.visit(_cbbtc);
552          
553          assertEquals("There should still be 1 error", 1, errors.size());  // Generated error is duplicate
554          assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", 
555                       errors.get(0).getFirst());
556          
557          //Check that a final variable cannot be reassigned to
558          s = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, 
559                                                                                   new Word(SourceInfo.NONE, "i")));
560          VariableDeclaration i = 
561            new VariableDeclaration(SourceInfo.NONE,  _packageMav, new UninitializedVariableDeclarator[]{uvd});
562          ExpressionStatement se = 
563            new ExpressionStatement(SourceInfo.NONE, 
564                                    new SimpleAssignmentExpression(SourceInfo.NONE, 
565                                                                   new SimpleNameReference(SourceInfo.NONE, 
566                                                                                           new Word(SourceInfo.NONE, "i")), 
567                                                                   new IntegerLiteral(SourceInfo.NONE, 2)));
568          ExpressionStatement se2 = 
569            new ExpressionStatement(SourceInfo.NONE, 
570                                    new SimpleAssignmentExpression(SourceInfo.NONE, 
571                                                                   new SimpleNameReference(SourceInfo.NONE, 
572                                                                                           new Word(SourceInfo.NONE, "i")), 
573                                                                   new IntegerLiteral(SourceInfo.NONE, 5)));
574          
575          BracedBody b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {i, se, se2, s});
576          ConcreteMethodDef cmd2 = 
577            new ConcreteMethodDef(SourceInfo.NONE, 
578                                  _publicMav, 
579                                  new TypeParameter[0], intt,
580                                  new Word(SourceInfo.NONE, "doubleAssignmentMethod"), 
581                                  new FormalParameter[0],
582                                  new ReferenceType[0], b);
583          
584          VariableData vdi = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, null);
585          MethodData md2 = new MethodData("doubleAssignmentMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
586                                          new VariableData[0], new String[0], _sd1, cmd2);
587          _sd1.addMethod(md2);
588          vdi.setEnclosingData(md2);
589    
590          md2.addVar(vdi);
591    
592          _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
593          cmd2.visit(_cbbtc);
594          assertEquals("There should now be 2 error2", 2, errors.size());
595          assertEquals("The error message should be correct", 
596                       "You cannot assign a value to i because it is immutable and has already been given a value", 
597                       errors.get(1).getFirst());
598     
599           
600          //test that if a variable is assigned in a branch of the if, and then returned, it is okay.
601          te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
602           new IntegerLiteral(SourceInfo.NONE, 5));
603          Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
604          Statement returnStatement = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
605          ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement, returnStatement}));
606          ift = new IfThenStatement(SourceInfo.NONE, te, ts);
607          
608          bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), 
609            ift, 
610            new ValueReturnStatement(SourceInfo.NONE, 
611                                     new IntegerLiteral(SourceInfo.NONE, 5))});
612          
613          ConcreteMethodDef cmd4 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], 
614                                       intt, new Word(SourceInfo.NONE, "myMethod3"), new FormalParameter[] {param}, 
615                                       new ReferenceType[0], bb);
616    
617          vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
618          vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
619          md1 = new MethodData("myMethod3", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
620                               new VariableData[] {vd1}, new String[0], _sd1, cmd4);
621          md1.addVar(vd1);
622          md1.addVar(vd2);
623          
624          vd1.setEnclosingData(md1);
625          vd2.setEnclosingData(md1);
626    
627          
628          _sd1.addMethod(md1);
629          _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
630          
631    
632          md1.addBlock(new BlockData(md1));
633          cmd4.visit(_cbbtc);
634          assertEquals("There should still be 2 errors", 2, errors.size());
635        }
636        
637        public void testCheckDifferentReturnTypes() {
638          SymbolData superSd = new SymbolData("aiya");
639          _sd1.setSuperClass(superSd);
640          MethodData md3 = new MethodData("methodName",
641                                          _publicMav,
642                                          new TypeParameter[0],
643                                          SymbolData.CHAR_TYPE,
644                                          new VariableData[0],
645                                          new String[0],
646                                          null,
647                                          null);
648          superSd.addMethod(md3);
649          MethodData md4 = new MethodData("methodName",
650                                         _publicMav,
651                                         new TypeParameter[0],
652                                         SymbolData.INT_TYPE,
653                                         new VariableData[0],
654                                         new String[0],
655                                         superSd,
656                                         null);
657          MethodDef mDef = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], new PrimitiveType(SourceInfo.NONE, "int"), 
658                                                 new Word(SourceInfo.NONE, "methodName"), new FormalParameter[0], new ReferenceType[0], 
659                                                 new BracedBody(SourceInfo.NONE, new BodyItemI[] {new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 76))}));
660          _sd1.addMethod(md4);
661          _cbbtc._symbolData = _sd1;
662          mDef.visit(_cbbtc);
663          assertEquals("There should be one error.", 1, errors.size());
664          assertEquals("The error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
665                       errors.get(0).getFirst());
666          mDef = new AbstractMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], new PrimitiveType(SourceInfo.NONE, "int"), 
667                                       new Word(SourceInfo.NONE, "methodName"), new FormalParameter[0], new ReferenceType[0]);
668          
669          mDef.visit(_cbbtc);
670          assertEquals("There should be two errors.", 2, errors.size());
671          assertEquals("The error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
672                       errors.get(1).getFirst());
673        }
674        
675        public void testForTypeOnly() {
676          Type t = new PrimitiveType(SourceInfo.NONE, "double");
677          t.visit(_cbbtc);
678          assertEquals("There should be no errors", 0, errors.size());
679          
680          SymbolData sd = new SymbolData("Adam");
681          sd.setIsContinuation(false);
682          symbolTable.put("Adam", sd);
683          sd.setMav(_publicMav);
684          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam", new Type[0]);
685          t.visit(_cbbtc);
686          assertEquals("There should still be no errors", 0, errors.size());
687          
688          SymbolData innerSd = new SymbolData("Adam$Wulf");
689          innerSd.setIsContinuation(false);
690          sd.addInnerClass(innerSd);
691          innerSd.setOuterData(sd);
692          innerSd.setMav(_publicMav);
693          _cbbtc.symbolTable.put("USaigehgihdsgslghdlighs", innerSd);
694          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
695          t.visit(_cbbtc);
696          assertEquals("There should still be no errors", 0, errors.size());
697          
698          innerSd.setMav(_privateMav);
699          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
700          t.visit(_cbbtc);
701          
702          String tcSD = _cbbtc._symbolData.getName();
703          assertEquals("There should be one error", 1, errors.size());
704          assertEquals("The error message should be correct", 
705                       "The class or interface Adam.Wulf in Adam.Wulf is private and cannot be accessed from " + tcSD,
706                       errors.get(0).getFirst());
707          
708          sd.setMav(_privateMav);
709          innerSd.setMav(_publicMav);
710          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
711          t.visit(_cbbtc);
712          assertEquals("There should be two errors", 2, errors.size());
713          assertEquals("The error message should be correct", 
714                         "The class or interface Adam in Adam is private and cannot be accessed from " + tcSD,
715                       errors.get(1).getFirst());
716        }
717        
718        public void testForConstructorDef() {
719          VariableDeclarator[] vds = 
720            new VariableDeclarator[] { new UninitializedVariableDeclarator(SourceInfo.NONE, 
721                                                                           new PrimitiveType(SourceInfo.NONE, "int"), 
722                                                                           new Word(SourceInfo.NONE, "i"))};
723          
724          VariableDeclaration vd =  new VariableDeclaration(SourceInfo.NONE, _finalMav, vds);
725          SimpleNameReference snr =  
726            new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i"));
727          ExpressionStatement es = 
728            new ExpressionStatement(SourceInfo.NONE, 
729                                    new SimpleAssignmentExpression(SourceInfo.NONE, 
730                                                                   snr, 
731                                                                   new IntegerLiteral(SourceInfo.NONE, 1)));      
732          BracedBody cbb = new BracedBody(SourceInfo.NONE, new BodyItemI[] { es });
733          ConstructorDef cd =  new ConstructorDef(SourceInfo.NONE, 
734                                                  new Word(SourceInfo.NONE, "Jimes"), 
735                                                  _publicMav, 
736                                                  new FormalParameter[0], 
737                                                  new ReferenceType[0], 
738                                                  cbb);
739          BracedBody b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {vd, cd});
740          ClassDef classDef = 
741            new ClassDef(SourceInfo.NONE, 
742                         _publicMav, 
743                         new Word(SourceInfo.NONE, "Jimes"), 
744                         new TypeParameter[0], 
745                         new ClassOrInterfaceType(SourceInfo.NONE, "java.io.StreamTokenizer", new Type[0]), 
746                         new ReferenceType[0], 
747                         b);
748    
749          SymbolData jimes = new SymbolData("Jimes");
750          VariableData vData = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, jimes);
751          _cbbtc._file = new File("Jimes.dj0");
752          jimes.setMav(_publicMav);
753          jimes.setIsContinuation(false);
754          jimes.addVar(vData);
755    
756          symbolTable.put("Jimes", jimes);
757    
758    //      SymbolData obj = _cbbtc.getSymbolData("java.lang.Object", new NullLiteral(SourceInfo.NONE), false, true);
759          SymbolData tokenizer = _cbbtc.getSymbolData("java.io.StreamTokenizer", new NullLiteral(SourceInfo.NONE), false, true);
760          jimes.setSuperClass(tokenizer);
761          SymbolData jutc = defineTestCaseClass();
762          
763          assert symbolTable.contains(tokenizer);
764          assert symbolTable.contains(jutc);
765          
766          MethodData md = 
767            new MethodData("Jimes", _publicMav, new TypeParameter[0], jimes, new VariableData[0], new String[0], jimes, cd);
768          MethodData objMd = 
769            new MethodData("java.lang.Object", _publicMav, new TypeParameter[0], tokenizer, new VariableData[0], 
770                           new String[0], tokenizer, cd);
771          jimes.addMethod(md);
772          
773          // assumes an explicit super call with no arguments
774          classDef.visit(_cbbtc);
775          assertEquals("There should be one error", 1, errors.size());
776          assertEquals("Error message should be correct", 
777                       "You must invoke one of java.io.StreamTokenizer's constructors here.  You can either explicitly "
778                         + "invoke one of its exisitng constructors or add a constructor with signature: StreamTokenizer().", 
779                       errors.getLast().getFirst());
780    
781          // test that a constructor can set the value of a final field
782          tokenizer.addMethod(objMd); //give super class constructor
783          vData.lostValue();
784    
785          classDef.visit(_cbbtc);
786          assertEquals("There should still be one error", 1, errors.size());
787    
788          // Since we are going to traverse classDef again, we are resetting the error log.
789          errors.clear();
790          // test that if the constructor does not assign a value to the final field, then an error is thrown
791          vData.lostValue();
792          cbb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {});
793          cd = new ConstructorDef(SourceInfo.NONE, 
794                                  new Word(SourceInfo.NONE, "Jimes"), 
795                                  _publicMav, 
796                                  new FormalParameter[0], 
797                                  new ReferenceType[0], 
798                                  cbb);
799          b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {vd, cd});
800          classDef = new ClassDef(SourceInfo.NONE, 
801                                  _publicMav, 
802                                  new Word(SourceInfo.NONE, "Jimes"), 
803                                  new TypeParameter[0], 
804                                  new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]), 
805                                  new ReferenceType[0], b);
806     
807    //      System.err.println("***** Starting traversal of classDef");
808          classDef.visit(_cbbtc);
809    //      System.err.println("Error 3 for line 803 of ClassBodyTypeChecker is: " + errors.get(2).getFirst());
810    //      System.err.println("Error 2 for line 803 of ClassBodyTypeChecker is: " + errors.get(1).getFirst());
811    //      System.err.println("Error 1 for line 803 of ClassBodyTypeChecker is: " + errors.get(0).getFirst());
812          
813          assertEquals("There should be 2 errors now", 2, errors.size());
814          
815          assertEquals("The second error message should be correct", 
816                       "The final field i has not been initialized.  Make sure you give it a value in this constructor", 
817                       errors.getLast().getFirst());
818          
819          //test the case of a constructor that makes a call to another constructor
820          vData = new VariableData("j", _finalMav, SymbolData.INT_TYPE, false, jimes);
821          jimes.setVars(new LinkedList<VariableData>());
822          jimes.addVar(vData);
823    
824          LinkedList<VariableData> vs = new LinkedList<VariableData>();
825          vs.addLast(vData);
826          _cbbtc = new ClassBodyTypeChecker(jimes, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), vs, new LinkedList<Pair<SymbolData, JExpression>>());
827          ExpressionStatement assign = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), new IntegerLiteral(SourceInfo.NONE, 45)));
828          b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new ExpressionStatement(SourceInfo.NONE, new SimpleThisConstructorInvocation(SourceInfo.NONE, new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]))), assign});
829          cd = new ConstructorDef(SourceInfo.NONE, new Word(SourceInfo.NONE, "name"), _publicMav, new FormalParameter[0], new ReferenceType[0], b);
830          cd.visit(_cbbtc);
831          assertEquals("There should now be 3 errors", 3, errors.size());
832          assertEquals("The error message should be correct","You cannot assign a value to j because it is immutable and has already been given a value" , errors.getLast().getFirst());
833    
834    
835        }
836        
837      }
838    }