Clover coverage report - Java Language Levels Test Coverage (javalanglevels-20120305-r5436)
Coverage timestamp: Sun Mar 4 2012 22:02:46 CST
file stats: LOC: 823   Methods: 38
NCLOC: 519   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
SpecialTypeChecker.java 73.4% 87% 76.3% 83.1%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.javalanglevels;
 38   
 39    import edu.rice.cs.javalanglevels.tree.*;
 40    import edu.rice.cs.javalanglevels.parser.JExprParser;
 41    import java.util.*;
 42    import java.io.File;
 43    import edu.rice.cs.plt.reflect.JavaVersion;
 44    import edu.rice.cs.plt.iter.*;
 45   
 46    import junit.framework.TestCase;
 47   
 48    /** SpecialTypeChecker is a base class for specialized type checkers such as BodyTypeChecker, ClassBodyTypeChecker,
 49    * ExpressionTypeChecker, etc. SpecialTypeChecker maintains the context.
 50    */
 51    public class SpecialTypeChecker extends TypeChecker {
 52    /** An incremental list of fields used because forward references among fields are not allowed.*/
 53    protected LinkedList<VariableData> _vars;
 54   
 55    /**Stores what variable datas have been newly given a value in this scope.*/
 56    protected LinkedList<VariableData> thingsThatHaveBeenAssigned;
 57   
 58    /**The context of this type checking--i.e. the data of the enclosing body.*/
 59    protected Data _data;
 60   
 61    /**The list of SymbolDatas corresponding to exceptions thrown in this body. */
 62    protected LinkedList<Pair<SymbolData, JExpression>> _thrown;
 63   
 64    /** Constructor for SpecialTypeChecker.
 65    * @param data The data that represents the context.
 66    * @param file The file that corresponds to the source file
 67    * @param packageName The string representing the package name
 68    * @param importedFiles The list of file names that have been specifically imported
 69    * @param importedPackages The list of package names that have been specifically imported
 70    * @param vars The list of fields that have been assigned up to the point where SpecialTypeChecker is called.
 71    * @param thrown The list of exceptions that the context is declared to throw
 72    */
 73  1279 public SpecialTypeChecker(Data data, File file, String packageName, LinkedList<String> importedFiles,
 74    LinkedList<String> importedPackages, LinkedList<VariableData> vars,
 75    LinkedList<Pair<SymbolData, JExpression>> thrown) {
 76  1279 super(file, packageName, importedFiles, importedPackages);
 77  0 if (vars == null) throw new RuntimeException("SpecialTypeChecker called with _vars = null!");
 78  1279 _data = data;
 79  1279 _vars = vars;
 80   
 81  1279 thingsThatHaveBeenAssigned = new LinkedList<VariableData>();
 82  1279 _thrown = thrown;
 83    }
 84   
 85    /** @return the enclosing data*/
 86  396 protected Data _getData() { return _data; }
 87   
 88    /** @return true iff the first enclosing MethodData or SymbolData is a static method. */
 89  351 protected boolean inStaticMethod() {
 90  351 for (Data d = _data; d != null; d = d.getOuterData()) {
 91  223 if (d instanceof MethodData) { return d.hasModifier("static"); }
 92    // else if (d instanceof SymbolData) { return false; }
 93    }
 94  128 return false;
 95    }
 96   
 97    /** Return the symbol data corresponding to the lhs and the namePiece, if possible.
 98    * Otherwise, return null.
 99    * @param lhs The left hand side of this complex reference, or null if this is a simple reference
 100    * @param namePiece The String right hand side of this reference
 101    * @param jexpr The JExpression corresponding to this class reference
 102    */
 103  122 protected SymbolData findClassReference(TypeData lhs, String namePiece, JExpression jexpr) {
 104  122 SymbolData result;
 105  122 if (lhs == null) {
 106    // Do not give an error if the SymbolData could not be found. This is done later.
 107  77 result = getSymbolData(true, namePiece, _getData(), jexpr, false);
 108   
 109    }
 110   
 111  45 else if (lhs instanceof PackageData) {
 112    // Do not give an error if the SymbolData could not be found
 113  43 result = getSymbolData(lhs.getName() + '.' + namePiece, _getData(), jexpr, false);
 114    }
 115   
 116    else {
 117    // Do not give error if it could not be found, but do give an error if the reference is ambiguous.
 118  2 result = getSymbolData(true, namePiece, lhs.getSymbolData(), jexpr, false);
 119   
 120    // Don't check for visibility here--check for it wherever this was called from.
 121    }
 122   
 123  122 return result;
 124   
 125    }
 126   
 127   
 128    /** TODO: Move this code to where it is needed?
 129    * Do any extra processing of this MethodInvocation, based on what level it is found at.
 130    * Here, check if the MethodData is declared to throw any exceptions, add them to the list of Exceptions
 131    * @param md The MethodData of the method being invoked
 132    * @param jexpr The jexpression corresponding to where this method is being invoked from.
 133    */
 134  2 protected void handleMethodInvocation(MethodData md, JExpression jexpr) {
 135  2 String[] thrown = md.getThrown();
 136  2 for (int i = 0; i<thrown.length; i++) {
 137  2 _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _data, jexpr), jexpr));
 138    }
 139    }
 140   
 141    /**
 142    * Given a ParenthesizedExpressionList, extract its expression array and return
 143    * an array with InstanceDatas for each type of the arguments. Throw an error if a non-instance type is
 144    * passed as an argument.
 145    * @param pel The ParenthesizedExpressionList the arguments are stored in.
 146    * @return The InstanceData[] corresponding to the types of the arguments
 147    */
 148  18 protected InstanceData[] getArgTypesForInvocation(ParenthesizedExpressionList pel) {
 149  18 Expression[] exprs = pel.getExpressions();
 150  18 InstanceData[] newArgs = new InstanceData[exprs.length];
 151  18 TypeData[] args = new TypeData[exprs.length];
 152  18 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
 153    _vars, _thrown);
 154  18 for (int i = 0; i < exprs.length; i++) {
 155  9 args[i] = exprs[i].visit(etc);
 156  0 if (args[i] == null || ! assertFound(args[i], exprs[i])) return null;
 157  9 if (!args[i].isInstanceType()) {
 158  0 _addError("Cannot pass a class or interface name as a constructor argument. " +
 159    "Perhaps you meant to create an instance or use " + args[i].getName() + ".class", exprs[i]);
 160    }
 161  9 newArgs[i] = args[i].getInstanceData(); // getInstanceData() is used in place of a cast
 162    }
 163  18 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
 164  18 return newArgs;
 165    }
 166   
 167    /** Makes sure that the initializedvariable declarator is correct (the types match)
 168    * Also, add the Variable Data corresponding to this initializiation to the _vars list, so
 169    * that it can be referenced within this scope.
 170    * Assumes typeRes is a SymbolData.
 171    * @param that The InitializedVariableDeclarator being visited
 172    * @param typeRes The TypeData (should be a SymbolData) corresponding to the type on the lhs of the assignment
 173    * @param nameRes Not used.
 174    * @param initializer_result The type of what we are initializing the varaible with
 175    */
 176  36 public TypeData forInitializedVariableDeclaratorOnly(InitializedVariableDeclarator that, TypeData typeRes,
 177    TypeData nameRes, TypeData initializer_result) {
 178  36 if (initializer_result != null && assertFound(initializer_result, that.getInitializer())) {
 179  36 if (!initializer_result.isInstanceType()) {
 180  1 _addError("Field or variable " + that.getName().getText() +
 181    " cannot be initialized with the class or interface name " + initializer_result.getName() +
 182    ". Perhaps you meant to create an instance or use " + initializer_result.getName() + ".class", that);
 183    }
 184    //we know typeRes is always a SymbolData.
 185  35 else if (!_isAssignableFrom(typeRes.getSymbolData(), initializer_result.getSymbolData())) {
 186  1 _addError("Type: \"" + typeRes.getName() + "\" expected, instead found type: \"" +
 187    initializer_result.getName() + '"', that);
 188    }
 189    }
 190  36 Word name = that.getName();
 191  36 String text = that.getName().getText();
 192  36 VariableData vd = _data.getVar(text);
 193  36 if (vd == null) {
 194  3 throw new RuntimeException("Internal Program Error: The field or variable " + text +
 195    " was not found in this block. Please report this bug.");
 196    }
 197  33 _vars.addLast(vd);
 198  33 return null;
 199    }
 200   
 201    /*This is not supported at any Language Level. It should have been caught during the first pass.*/
 202  0 public TypeData forInstanceInitializer(InstanceInitializer that) {
 203  0 throw new RuntimeException("Internal Program Error: Instance Initializers are not supported." +
 204    " This should have been caught before the Type Checker Pass. Please report this bug.");
 205    }
 206   
 207    /*This is not supported at any Language Level. It should have been caught during the first pass.*/
 208  0 public TypeData forStaticInitializer(StaticInitializer that) {
 209  0 throw new RuntimeException("Internal Program Error: Static Initializers are not supported." +
 210    " This should have been caught before the Type Checker Pass. Please report this bug.");
 211    }
 212   
 213    /*This is not supported at any Language Level. It should have been caught during the first pass.*/
 214  0 public TypeData forLabeledStatement(LabeledStatement that) {
 215  0 throw new RuntimeException("Internal Program Error: Labeled Statements are not supported." +
 216    " This should have been caught before the Type Checker Pass. Please report this bug.");
 217    }
 218   
 219    /** Visits the expression with a new ExpressionTypeChecker, and return the result of that visitation.
 220    * Keep track of what variables get values within the expression.
 221    * @param that The ExpressionStatement we are visiting.
 222    * @return The result of visiting the expression with the ExpressionTypeChecker.
 223    */
 224  74 public TypeData forExpressionStatement(ExpressionStatement that) {
 225  74 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
 226    _vars, _thrown);
 227  74 final TypeData exprRes = that.getExpression().visit(etc);
 228   
 229    //do this so that we can keep track of anything that got assigned
 230  74 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
 231   
 232  74 return exprRes;
 233    }
 234   
 235    /**
 236    * Visit the ThrowStatement's expression to determine what type of Exception is being thrown.
 237    * Add the corresponding SymbolData to _thrown.
 238    */
 239  33 public TypeData forThrowStatement(ThrowStatement that) {
 240  33 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
 241    _vars, _thrown);
 242  33 final TypeData thrown_result = that.getThrown().visit(etc);
 243  33 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
 244   
 245  33 forThrowStatementOnly(that, thrown_result);
 246  33 return SymbolData.EXCEPTION.getInstanceData();
 247    }
 248   
 249   
 250    /** Make sure that what is being thrown is an instantiation of a class, not a class name, and that
 251    * it extends Throwable. Otherwise, give an error.
 252    * @param that The throw statement we are visiting
 253    * @param thrown_result The TypeData result of visiting the throw statement. It should be an InstanceData,
 254    * unless there is an error in the student's code.
 255    */
 256  35 public TypeData forThrowStatementOnly(ThrowStatement that, TypeData thrown_result) {
 257  0 if (thrown_result == null || ! assertFound(thrown_result, that.getThrown())) return null;
 258   
 259    // add the SymbolData even if we're throwing a SymbolData, not an InstanceData
 260  35 _thrown.addLast(new Pair<SymbolData, JExpression>(thrown_result.getSymbolData(), that));
 261   
 262    //make sure they instantiated what is being thrown.
 263  35 if (!thrown_result.isInstanceType()) {
 264  1 _addError("You cannot throw a class or interface name. Perhaps you mean to instantiate the exception class " +
 265    thrown_result.getSymbolData().getName() + " that you are throwing", that);
 266  1 thrown_result = thrown_result.getInstanceData();
 267    }
 268   
 269    // System.err.println("getSymbolData(\"java.lang.Throwable\", that, false, true) = " +
 270    // getSymbolData("java.lang.Throwable", that, false, true));
 271    //make sure what is being thrown extends java.lang.Throwable.
 272  35 if (! _isAssignableFrom(getSymbolData("java.lang.Throwable", that, false, true), thrown_result.getSymbolData())) {
 273  1 _addError("You are attempting to throw " + thrown_result.getSymbolData().getName() +
 274    ", which does not implement the Throwable interface", that);
 275    }
 276  35 return thrown_result;
 277    }
 278   
 279    /*This is not supported at any Language Level. It should have been caught during the first pass.*/
 280  0 public TypeData forSynchronizedStatement(SynchronizedStatement that) {
 281  0 throw new RuntimeException("SynchronizedStatements are not supported.");
 282    }
 283   
 284   
 285    /* Visit the declarator of this formal parameter and return its result.
 286    * @param that The Formal Parameter we are visiting.
 287    */
 288  0 public TypeData forFormalParameter(FormalParameter that) {
 289  0 final TypeData declarator_result = that.getDeclarator().visit(this);
 290  0 return declarator_result;
 291    }
 292   
 293    /** Visit each of the declarators of this declaration.
 294    * @param that The VariableDeclaration we are visiting.
 295    */
 296  113 public TypeData forVariableDeclaration(VariableDeclaration that) {
 297  113 final TypeData mavRes = that.getMav().visit(this);
 298  113 final TypeData[] declarators_result = makeArrayOfRetType(that.getDeclarators().length);
 299  113 for (int i = 0; i < that.getDeclarators().length; i++) {
 300  128 declarators_result[i] = that.getDeclarators()[i].visit(this);
 301    }
 302  113 return null;
 303    }
 304   
 305    /** If this VariableDeclarator is uninitialized, make sure its type can be resolved and visit
 306    * its name.
 307    * @param that The UninitializedVariableDeclarator we are visiting.
 308    */
 309  103 public TypeData forUninitializedVariableDeclarator(UninitializedVariableDeclarator that) {
 310  103 final TypeData typeRes = getSymbolData(that.getType().getName(), _data, that.getType());
 311  103 final TypeData nameRes = that.getName().visit(this);
 312  103 return forUninitializedVariableDeclaratorOnly(that, typeRes, nameRes);
 313    }
 314   
 315    /** If the VariableDeclarator is initialized, things get a little bit more complicated.
 316    * Resolve the type and visit the name, like we do for the uninitilized case.
 317    * Then, check to see if the initializer is an array initializer. If so, delegate.
 318    * Otherwise, just visit it with an ExpressionTypeChecker.
 319    * @param that The InitializedVariableDeclarator we are visiting.
 320    */
 321  33 public TypeData forInitializedVariableDeclarator(InitializedVariableDeclarator that) {
 322  33 final SymbolData typeRes = getSymbolData(that.getType().getName(), _data, that.getType());
 323  33 final TypeData nameRes = that.getName().visit(this); //we think this is always null
 324   
 325  33 TypeData initializer_result;
 326  33 if (that.getInitializer() instanceof ArrayInitializer) {
 327  1 initializer_result = forArrayInitializerHelper((ArrayInitializer) that.getInitializer(), typeRes);
 328    }
 329    else {
 330  32 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
 331    _vars, _thrown);
 332  32 initializer_result = that.getInitializer().visit(etc);
 333  32 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned); //incorporate this list here
 334    }
 335  33 return forInitializedVariableDeclaratorOnly(that, typeRes, nameRes, initializer_result);
 336    }
 337   
 338    /** A variable data can be assigned to if it is not final or if it does not have a value.
 339    * (i.e. final variables that already have a value cannot be assigned to. Everything else can be).
 340    */
 341  0 protected boolean canBeAssigned(VariableData vd) {
 342  0 return !vd.isFinal() || !vd.hasValue();
 343    }
 344   
 345    /** Makes sure that the specified type is an array type, and then
 346    * examines the elements in the array initializer and makes sure each has a type assignable to
 347    * the elementType of the specified array type. Returns an instance data corresponding to the type of the array.
 348    */
 349  17 public TypeData forArrayInitializerHelper(ArrayInitializer that, SymbolData type) {
 350  1 if (type == null) {return null;}
 351  2 if (!(type instanceof ArrayData)) {_addError("You cannot initialize the non-array type " + type.getName() +
 352  2 " with an array initializer", that); return type.getInstanceData();}
 353   
 354  14 SymbolData elementType = ((ArrayData) type).getElementType();
 355  14 VariableInitializerI[] elements = that.getItems();
 356  14 TypeData[] result = makeArrayOfRetType(elements.length);
 357   
 358  14 for (int i = 0; i<elements.length; i++) {
 359  44 if (elements[i] instanceof ArrayInitializer) {
 360  2 result[i] = forArrayInitializerHelper((ArrayInitializer) elements[i], elementType);
 361    }
 362    else {
 363  42 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
 364    _vars, _thrown);
 365  42 result[i] = elements[i].visit(etc);
 366   
 367    // Incorporate the things that were assigned in the expression here
 368  42 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
 369   
 370  42 if (result[i] != null) {
 371  42 if (assertFound(result[i], (JExpression) that.getItems()[i])) {
 372  42 if (!result[i].getSymbolData().isAssignableTo(elementType, LanguageLevelConverter.OPT.javaVersion())) {
 373  5 _addError("The elements of this initializer should have type " + elementType.getName() + " but element "
 374    + i + " has type " + result[i].getSymbolData().getName(), (JExpression) that.getItems()[i]);
 375    }
 376    else {
 377  37 assertInstanceType(result[i], "The elements of this initializer should all be instances," +
 378    " but you have specified the type name " + result[i].getName(),
 379    (JExpression) that.getItems()[i]);
 380    }
 381    }
 382    }
 383    }
 384    }
 385  14 return type.getInstanceData();
 386    }
 387   
 388    /** Look up the SymbolData for this InnerClass within the enclosing data, check for cyclic inheritance,
 389    * and then visit everything inside the inner class.
 390    * @param that The InnerClassDef we're visiting
 391    */
 392  2 public TypeData forInnerClassDef(InnerClassDef that) {
 393  2 String className = that.getName().getText();
 394   
 395    // This works because className will never be a qualified name
 396  2 SymbolData sd = _data.getInnerClassOrInterface(className);
 397   
 398  0 if (sd == null) throw new RuntimeException("SymbolData is null for class name = " + className);
 399   
 400    // Check for cyclic inheritance
 401  0 if (checkForCyclicInheritance(sd, new LinkedList<SymbolData>(), that)) { return null; }
 402  2 final TypeData mavRes = that.getMav().visit(this);
 403  2 final TypeData nameRes = that.getName().visit(this);
 404  2 final TypeData[] typeParamRes = makeArrayOfRetType(that.getTypeParameters().length);
 405  2 for (int i = 0; i < that.getTypeParameters().length; i++) {
 406  0 typeParamRes[i] = that.getTypeParameters()[i].visit(this);
 407    }
 408  2 final TypeData superClassRes = that.getSuperclass().visit(this);
 409  2 final TypeData[] interfacesRes = makeArrayOfRetType(that.getInterfaces().length);
 410  2 for (int i = 0; i < that.getInterfaces().length; i++) {
 411  0 interfacesRes[i] = that.getInterfaces()[i].visit(this);
 412    }
 413  2 final TypeData bodyRes =
 414    that.getBody().visit(new ClassBodyTypeChecker(sd, _file, _package, _importedFiles, _importedPackages, _vars,
 415    _thrown));
 416  2 return null;
 417    }
 418   
 419    /** Look for the inner interface inside of the enclosing data.
 420    * Then, visit everything that needs to be visited.
 421    * @param that The InnerInterfaceDef that is being visited.
 422    */
 423  0 public TypeData forInnerInterfaceDef(InnerInterfaceDef that) {
 424  0 String className = that.getName().getText();
 425  0 SymbolData sd = _data.getInnerClassOrInterface(className); // className is a relative name
 426    // if (sd == null) System.out.println("Tried to look up " + className + " in " + _data.getName()
 427    // + " but I got back null");
 428   
 429    // Check for cyclic inheritance
 430  0 if (checkForCyclicInheritance(sd, new LinkedList<SymbolData>(), that)) {
 431  0 return null;
 432    }
 433  0 final TypeData mavRes = that.getMav().visit(this);
 434  0 final TypeData nameRes = that.getName().visit(this);
 435  0 final TypeData[] typeParamRes = makeArrayOfRetType(that.getTypeParameters().length);
 436  0 for (int i = 0; i < that.getTypeParameters().length; i++) {
 437  0 typeParamRes[i] = that.getTypeParameters()[i].visit(this);
 438    }
 439  0 final TypeData[] interfacesRes = makeArrayOfRetType(that.getInterfaces().length);
 440  0 for (int i = 0; i < that.getInterfaces().length; i++) {
 441  0 interfacesRes[i] = that.getInterfaces()[i].visit(this);
 442    }
 443   
 444  0 final TypeData bodyRes =
 445    that.getBody().visit(new InterfaceBodyTypeChecker(sd, _file, _package, _importedFiles, _importedPackages, _vars,
 446    _thrown));
 447  0 return null;
 448    }
 449   
 450   
 451    /** Compare the two lists of variable datas, and if a data is in both lists, mark it as having been assigned.
 452    * @param l1 One of the lists of variable datas
 453    * @param l2 The other list of variable datas.
 454    */
 455  27 void reassignVariableDatas(LinkedList<VariableData> l1, LinkedList<VariableData> l2) {
 456  27 for (int i = 0; i<l1.size(); i++) {
 457  4 if (l2.contains(l1.get(i))) {
 458  1 l1.get(i).gotValue();
 459  1 l1.get(i).gotValue();
 460    }
 461    }
 462    }
 463   
 464    /** Compare a list of variable datas and a list of list of variable datas.
 465    * If a variable data is in the list and in each list of the lists of lists, mark it as having been
 466    * assigned.
 467    * @param tryBlock The list of variable datas.
 468    * @param catchBlocks The list of list of variable datas.
 469    */
 470  29 void reassignLotsaVariableDatas(LinkedList<VariableData> tryBlock, LinkedList<LinkedList<VariableData>> catchBlocks) {
 471  29 for (int i = 0; i<tryBlock.size(); i++) {
 472  0 boolean seenIt = true;
 473  0 for (int j = 0; j<catchBlocks.size(); i++) {
 474  0 if (!catchBlocks.get(j).contains(tryBlock.get(i))) {seenIt = false;}
 475    }
 476  0 if (seenIt) { //find the variable data in vars and give it a value!
 477  0 tryBlock.get(i).gotValue();
 478    }
 479    }
 480    }
 481   
 482    /** If an exception is thrown but not caught, throw the appropriate error, based on the JExpression.*/
 483  15 public void handleUncheckedException(SymbolData sd, JExpression j) {
 484    // System.err.println("***** handleUncheckedException called for " + sd + " and " + j);
 485  15 if (j instanceof MethodInvocation) {
 486  3 _addError("The method " + ((MethodInvocation)j).getName().getText() + " is declared to throw the exception " +
 487    sd.getName() + " which needs to be caught or declared to be thrown", j);
 488    }
 489  12 else if (j instanceof ThrowStatement) {
 490  7 _addError("This statement throws the exception " + sd.getName() +
 491    " which needs to be caught or declared to be thrown", j);
 492    }
 493  5 else if (j instanceof ClassInstantiation) {
 494  1 _addError("The constructor for the class " + ((ClassInstantiation)j).getType().getName() +
 495    " is declared to throw the exception " + sd.getName() +
 496    " which needs to be caught or declared to be thrown.", j);
 497    }
 498  4 else if (j instanceof SuperConstructorInvocation) {
 499  2 _addError("The constructor of this class's super class could throw the exception " + sd.getName() +
 500    ", so the enclosing constructor needs to be declared to throw it", j);
 501    }
 502  2 else if (j instanceof ThisConstructorInvocation) {
 503  1 _addError("This constructor could throw the exception " + sd.getName() +
 504    ", so this enclosing constructor needs to be declared to throw it", j);
 505    }
 506   
 507  1 else if (j instanceof BracedBody) { //then this is because of an implicit super constructor reference.
 508  1 _addError("There is an implicit call to the superclass's constructor here. " +
 509    "That constructor could throw the exception " + sd.getName() +
 510    ", so the enclosing constructor needs to be declared to throw it", j);
 511    }
 512   
 513    else {
 514  0 throw new RuntimeException("Internal Program Error: Something besides a method invocation or throw statement" +
 515    " threw an exception. Please report this bug.");
 516    }
 517    }
 518   
 519    /** Returns whether the sd is a checked exception, i.e. one that needs to be caught or declared to be thrown.
 520    * This is defined as all subclasses of java.lang.Throwable except for subclasses of java.lang.RuntimeException
 521    */
 522  85 public boolean isCheckedException(SymbolData sd, JExpression that) {
 523  85 return sd.isSubClassOf(getSymbolData("java.lang.Throwable", _data, that, false)) &&
 524    ! sd.isSubClassOf(getSymbolData("java.lang.RuntimeException", _data, that, false)) &&
 525    ! sd.isSubClassOf(getSymbolData("java.lang.Error", _data, that, false));
 526    }
 527   
 528    /** Return true if the Exception is a checked exception yet is not caught or declared to be thrown, and false
 529    * otherwise. An exception is a checked if it does not extend either java.lang.RuntimeException or java.lang.Error,
 530    * and is not declared to be thrown by the enclosing method.
 531    * @param sd The SymbolData of the Exception we are checking.
 532    * @param that The JExpression passed to getSymbolData for error purposes.
 533    */
 534  0 public boolean isUncaughtCheckedException(SymbolData sd, JExpression that) {
 535  0 return isCheckedException(sd, that);
 536    }
 537   
 538    /** Visit each of the items in the body and make sure that none throw uncaught exceptions */
 539  358 public TypeData forBody(Body that) {
 540  358 final TypeData[] items_result = makeArrayOfRetType(that.getStatements().length);
 541  358 for (int i = 0; i < that.getStatements().length; i++) {
 542  456 items_result[i] = that.getStatements()[i].visit(this);
 543    //walk over what has been thrown and throw an error if it contains an unchecked exception
 544  456 for (int j = 0; j < this._thrown.size(); j++) {
 545  23 if (isUncaughtCheckedException(this._thrown.get(j).getFirst(), that)) {
 546  3 handleUncheckedException(this._thrown.get(j).getFirst(), this._thrown.get(j).getSecond());
 547    }
 548    }
 549    }
 550   
 551  358 return forBodyOnly(that, items_result);
 552    }
 553   
 554    /** Delegate to forBody*/
 555  319 public TypeData forBracedBody(BracedBody that) { return forBody(that); }
 556   
 557    /** Delegate to forBody*/
 558  39 public TypeData forUnbracedBody(UnbracedBody that) { return forBody(that); }
 559   
 560    /** Test the methods defined in the enclosing class. */
 561    public static class BobTest extends TestCase {
 562   
 563    private SpecialTypeChecker _stc;
 564   
 565    private SymbolData _sd1;
 566    private SymbolData _sd2;
 567    private SymbolData _sd3;
 568    private SymbolData _sd4;
 569    private SymbolData _sd5;
 570    private SymbolData _sd6;
 571   
 572  0 public BobTest() { this(""); }
 573  5 public BobTest(String name) { super(name); }
 574   
 575  5 public void setUp() {
 576  5 errors = new LinkedList<Pair<String, JExpressionIF>>();
 577  5 LanguageLevelConverter.symbolTable.clear();
 578  5 _stc = new SpecialTypeChecker(null, new File(""), "", new LinkedList<String>(), new LinkedList<String>(),
 579    new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
 580  5 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
 581  5 _stc._importedPackages.addFirst("java.lang");
 582  5 _sd1 = new SymbolData("i.like.monkey");
 583  5 _sd2 = new SymbolData("i.like.giraffe");
 584  5 _sd3 = new SymbolData("zebra");
 585  5 _sd4 = new SymbolData("u.like.emu");
 586  5 _sd5 = new SymbolData("");
 587  5 _sd6 = new SymbolData("cebu");
 588  5 _stc._data = _sd1;
 589    }
 590   
 591  1 public void testForInitializedVariableDeclarator() {
 592  1 LanguageLevelVisitor llv =
 593    new LanguageLevelVisitor(_stc._file,
 594    _stc._package,
 595    null, // enclosingClassName for top level traversal.
 596    _stc._importedFiles,
 597    _stc._importedPackages,
 598    new HashSet<String>(),
 599    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
 600    new LinkedList<Command>());
 601   
 602    // LanguageLevelConverter.symbolTable.clear();
 603   
 604  1 SourceInfo si = SourceInfo.NONE;
 605  1 Expression e1 = new IntegerLiteral(si, 1);
 606  1 Expression e2 = new IntegerLiteral(si, 2);
 607  1 Expression e3 = new PlusExpression(si, new IntegerLiteral(si, 3), new CharLiteral(si, 'e'));
 608  1 Expression e4 = new CharLiteral(si, 'c');
 609   
 610  1 ArrayType intArrayType =
 611    new ArrayType(SourceInfo.NONE, "int[]", new PrimitiveType(SourceInfo.NONE, "int"));
 612   
 613    //make sure it works -- most testing done in testArrayInitializerHelper
 614  1 ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
 615  1 intArray.setIsContinuation(false);
 616  1 symbolTable.remove("int[]");
 617  1 symbolTable.put("int[]", intArray);
 618   
 619  1 _stc._data.addVar(new VariableData("foozle", _publicMav, intArray, false, _stc._data));
 620  1 InitializedVariableDeclarator ivd =
 621    new InitializedVariableDeclarator(SourceInfo.NONE, intArrayType,
 622    new Word(SourceInfo.NONE, "foozle"),
 623    new ArrayInitializer(si, new VariableInitializerI[] {e1, e2, e3, e4}));
 624   
 625  1 assertEquals("Should return null", null, ivd.visit(_stc));
 626  1 assertEquals("There should be no errors", 0, errors.size());
 627    }
 628   
 629  1 public void testForInitializedVariableDeclaratorOnly() {
 630  1 SymbolData sd1 = SymbolData.DOUBLE_TYPE;
 631  1 SymbolData sd2 = SymbolData.BOOLEAN_TYPE;
 632  1 SymbolData sd3 = SymbolData.INT_TYPE;
 633  1 _stc._data.addVar(new VariableData("j", _publicMav, SymbolData.DOUBLE_TYPE, false, _stc._data));
 634   
 635  1 InitializedVariableDeclarator ivd =
 636    new InitializedVariableDeclarator(SourceInfo.NONE,
 637    JExprParser.NO_TYPE,
 638    new Word(SourceInfo.NONE, "j"),
 639    new DoubleLiteral(SourceInfo.NONE, 1.0));
 640   
 641   
 642  1 assertEquals("Two assignable types should not throw an error; return null.", null,
 643    _stc.forInitializedVariableDeclaratorOnly(ivd, sd1, sd1, sd3.getInstanceData()));
 644  1 assertEquals("Should be no errors", 0, errors.size());
 645   
 646  1 assertEquals("Two unassignable types should throw an error; return null.", null,
 647    _stc.forInitializedVariableDeclaratorOnly(ivd, sd1, sd1, sd2.getInstanceData()));
 648  1 assertEquals("Should now be one error", 1, errors.size());
 649  1 assertEquals("Error message should be correct:", "Type: \"double\" expected, instead found type: \"boolean\"",
 650    errors.getLast().getFirst());
 651   
 652  1 SymbolData foo = new SymbolData("Foo");
 653  1 assertEquals("An initialization from a SymbolData should return null", null,
 654    _stc.forInitializedVariableDeclaratorOnly(ivd, sd1, null, foo));
 655  1 assertEquals("There should be 2 errors", 2, errors.size());
 656  1 assertEquals("Error message should be correct:",
 657    "Field or variable j cannot be initialized with the class or interface name Foo. " +
 658    "Perhaps you meant to create an instance or use Foo.class", errors.getLast().getFirst());
 659    }
 660   
 661  1 public void testForThrowStatementOnly() {
 662  1 ThrowStatement s = new ThrowStatement(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE));
 663  1 SymbolData exception = _stc.getSymbolData("java.lang.Throwable", s, false, true);
 664  1 InstanceData exceptionInstance = exception.getInstanceData();
 665   
 666  1 SymbolData notAnException = new SymbolData("bob");
 667  1 InstanceData naeInstance = notAnException.getInstanceData();
 668   
 669   
 670  1 assertEquals("When a SymbolData is the thrown type, return its InstanceData", exceptionInstance,
 671    _stc.forThrowStatementOnly(s, exception));
 672  1 assertEquals("There should be 1 error", 1, errors.size());
 673   
 674  1 assertEquals("Error message should be correct",
 675    "You cannot throw a class or interface name. " +
 676    "Perhaps you mean to instantiate the exception class java.lang.Throwable that you are throwing",
 677    errors.get(0).getFirst());
 678   
 679  1 assertEquals("When a thrown type does not implement Throwable, return the type anyway", naeInstance,
 680    _stc.forThrowStatementOnly(s, naeInstance));
 681  1 assertEquals("There should be 2 errors", 2, errors.size());
 682  1 assertEquals("Error message should be correct",
 683    "You are attempting to throw bob, which does not implement the Throwable interface",
 684    errors.getLast().getFirst());
 685    }
 686   
 687   
 688  1 public void testForArrayInitializerHelper() {
 689  1 LanguageLevelVisitor llv =
 690    new LanguageLevelVisitor(_stc._file, _stc._package,
 691    null /* enclosingClassName */,
 692    _stc._importedFiles,
 693    _stc._importedPackages,
 694    new HashSet<String>(),
 695    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
 696    new LinkedList<Command>());
 697    // LanguageLevelConverter.symbolTable = llv.symbolTable = _stc.symbolTable;
 698   
 699  1 SourceInfo si = SourceInfo.NONE;
 700   
 701  1 Expression e1 = new IntegerLiteral(si, 1);
 702  1 Expression e2 = new IntegerLiteral(si, 2);
 703  1 Expression e3 = new PlusExpression(si, new IntegerLiteral(si, 3), new CharLiteral(si, 'e'));
 704  1 Expression e4 = new CharLiteral(si, 'c');
 705  1 Expression e5 = new DoubleLiteral(si, 5.8);
 706  1 Expression e6 = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int"));
 707   
 708  1 ArrayInitializer a1 = new ArrayInitializer(si, new VariableInitializerI[] {e1, e3, e4});
 709  1 ArrayInitializer a2 = new ArrayInitializer(si, new VariableInitializerI[] {e2, e3, e1});
 710   
 711  1 Expression nl = new NullLiteral(si);
 712   
 713    //it works for a one dimensional array
 714  1 ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
 715  1 intArray.setIsContinuation(false);
 716  1 symbolTable.remove("int[]");
 717  1 symbolTable.put("int[]", intArray);
 718   
 719  1 ArrayInitializer ia = new ArrayInitializer(si, new VariableInitializerI[] {e1, e2, e3, e4});
 720  1 assertEquals("Should return instance of int[]", intArray.getInstanceData(),
 721    _stc.forArrayInitializerHelper(ia, intArray));
 722  1 assertEquals("There should be no errors", 0, errors.size());
 723   
 724    //it works for a 2 dimensional array
 725  1 ArrayData intArray2 = new ArrayData(intArray, llv, si);
 726  1 intArray2.setIsContinuation(false);
 727  1 symbolTable.put("int[][]", intArray2);
 728   
 729  1 ia = new ArrayInitializer(si, new VariableInitializerI[]{a1, a2});
 730  1 assertEquals("Should return instance of int[][]", intArray2.getInstanceData(),
 731    _stc.forArrayInitializerHelper(ia, intArray2));
 732  1 assertEquals("There should be no errors", 0, errors.size());
 733   
 734    //it works for a 2 dimensional array with null as its elements
 735  1 ia = new ArrayInitializer(si, new VariableInitializerI[] {nl, nl});
 736  1 assertEquals("Should return instance of int[][]", intArray2.getInstanceData(),
 737    _stc.forArrayInitializerHelper(ia, intArray2));
 738   
 739    //throw an error if the type passed to the helper is not an array data
 740  1 assertEquals("Should return double", SymbolData.DOUBLE_TYPE.getInstanceData(),
 741    _stc.forArrayInitializerHelper(ia, SymbolData.DOUBLE_TYPE));
 742  1 assertEquals("There should be one error message", 1, errors.size());
 743  1 assertEquals("The error message should be correct",
 744    "You cannot initialize the non-array type double with an array initializer",
 745    errors.getLast().getFirst());
 746   
 747    //throw an error if the type of one of the elements doesn't match
 748  1 ia = new ArrayInitializer(si, new VariableInitializerI[] {e1, e2, e5, e4});
 749  1 assertEquals("Should return instance of int[]", intArray.getInstanceData(),
 750    _stc.forArrayInitializerHelper(ia, intArray));
 751  1 assertEquals("There should be two error messages", 2, errors.size());
 752  1 assertEquals("The error message should be correct",
 753    "The elements of this initializer should have type int but element 2 has type double",
 754    errors.getLast().getFirst());
 755   
 756    //throw an error if null in 1 dimensional int array
 757  1 ia = new ArrayInitializer(si, new VariableInitializerI[] {nl, nl});
 758  1 assertEquals("Should return instance of int[]", intArray.getInstanceData(),
 759    _stc.forArrayInitializerHelper(ia, intArray));
 760  1 assertEquals("There should be four error messages", 4, errors.size());
 761  1 assertEquals("The error message should be correct",
 762    "The elements of this initializer should have type int but element 0 has type null",
 763    errors.get(2).getFirst());
 764  1 assertEquals("The error message should be correct",
 765    "The elements of this initializer should have type int but element 1 has type null",
 766    errors.get(3).getFirst());
 767   
 768    //should throw error if type name is passed instead of instance
 769  1 ia = new ArrayInitializer(si, new VariableInitializerI[] {e1, e2, e3, e4, e6});
 770  1 assertEquals("Should return instance of int[]", intArray.getInstanceData(),
 771    _stc.forArrayInitializerHelper(ia, intArray));
 772  1 assertEquals("Should now be 5 error messages", 5, errors.size());
 773  1 assertEquals("Error message should be correct",
 774    "The elements of this initializer should all be instances, but you have specified the type name" +
 775    " int. Perhaps you meant to create a new instance of int",
 776    errors.getLast().getFirst());
 777    }
 778   
 779  1 public void testFindClassReference() {
 780  1 SymbolData string = new SymbolData("java.lang.String");
 781  1 string.setIsContinuation(false);
 782  1 string.setPackage("java.lang");
 783  1 symbolTable.remove("java.lang.String");
 784  1 symbolTable.put("java.lang.String", string);
 785   
 786    //if lhs is null, just look up SymbolData
 787  1 assertEquals("Should return string", string,
 788    _stc.findClassReference(null, "java.lang.String", new NullLiteral(SourceInfo.NONE)));
 789  1 assertEquals("Should not be an error", 0, errors.size());
 790   
 791    //if SymbolData cannot be found, do not add error--just return null
 792  1 assertEquals("Should return null", null,
 793    _stc.findClassReference(null, "non-existant", new NullLiteral(SourceInfo.NONE)));
 794  1 assertEquals("Should be no errors", 0, errors.size());
 795   
 796    //if LHS is package data, try to look up fully qualified name
 797  1 assertEquals("Should return string", string,
 798    _stc.findClassReference(new PackageData("java.lang"), "String", new NullLiteral(SourceInfo.NONE)));
 799  1 assertEquals("Should not be an error", 0, errors.size());
 800   
 801    //if symbol data cannot be found, do not give error
 802  1 assertEquals("Should return null", null,
 803    _stc.findClassReference(new PackageData("nonsense"), "non-existant",
 804    new NullLiteral(SourceInfo.NONE)));
 805  1 assertEquals("Should be no errors", 0, errors.size());
 806   
 807    //otherwise, try to look up symbolData from context of lhs
 808  1 SymbolData inner = new SymbolData("java.lang.String$Inner");
 809  1 inner.setIsContinuation(false);
 810  1 inner.setPackage("java.lang");
 811  1 inner.setOuterData(string);
 812  1 string.addInnerClass(inner);
 813  1 assertEquals("Should return inner", inner,
 814    _stc.findClassReference(string, "Inner", new NullLiteral(SourceInfo.NONE)));
 815  1 assertEquals("Should be no errors", 0, errors.size());
 816   
 817    //do not give error if it could not be found
 818  1 assertEquals("Should return null", null,
 819    _stc.findClassReference(string, "non-existant", new NullLiteral(SourceInfo.NONE)));
 820  1 assertEquals("Should be no errors", 0, errors.size());
 821    }
 822    }
 823    }