Code Search for Developers
 
 
  

ClassBodyTypeChecker.java from DrJava at Krugle


Show ClassBodyTypeChecker.java syntax highlighted

/*BEGIN_COPYRIGHT_BLOCK
 *
 * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software is Open Source Initiative approved Open Source Software.
 * Open Source Initative Approved is a trademark of the Open Source Initiative.
 * 
 * This file is part of DrJava.  Download the current version of this project
 * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 * 
 * END_COPYRIGHT_BLOCK*/

package edu.rice.cs.javalanglevels;

import edu.rice.cs.javalanglevels.tree.*;
import edu.rice.cs.javalanglevels.parser.JExprParser;
import java.util.*;
import java.io.*;
import edu.rice.cs.plt.reflect.JavaVersion;

import junit.framework.TestCase;

/**
 * Do the TypeChecking appropriate to the context of a class body.  Common to all Language Levels.
 */
public class ClassBodyTypeChecker extends Bob {
  
  /**The SymbolData corresponding to this class.*/
  private SymbolData _symbolData;
  
  /**True if we encounter a ConstructorDef while visiting the body.*/
  protected boolean hasConstructor;
  
  /*
   * Constructor for ClassBodyTypeChecker.
   * Adds all the variables in the symbol data to the list of what can be seen from this context,
   * since class fields can always be seen.
   * 
   * @param sd  The SymbolData of the class we are type checking.
   * @param file  The File corresponding to the source file we are checking.
   * @param packageName  The package of the source file.
   * @param importedFiles  A list of the names of the classes that are specifically imported in the source file
   * @param importedPackages  A list of the names of the packages that are imported in the source file.
   * @param vars  A list of the variable datas that can be seen and have been given a value before this context
   * @param thrown  The exceptions that are thrown
   */
  public ClassBodyTypeChecker(SymbolData sd, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
    super(sd, file, packageName, importedFiles, importedPackages, vars, thrown);
    _symbolData = sd;
    hasConstructor = false;
    
    _vars.addAll(sd.getVars());
    
    LinkedList<VariableData> superVars = sd.getAllSuperVars();
    for (int i = 0; i<superVars.size(); i++) {
      VariableData tempVD = superVars.get(i);
      if (tempVD.isFinal()) {
        if (tempVD.gotValue()) {
          thingsThatHaveBeenAssigned.addLast(tempVD);
        }
      }
    }
    _vars.addAll(superVars);

  }
  
  /*@return the symbol data corresponding to the class we are visiting*/
  protected Data _getData() { return _symbolData; }

  
  /**
   * We need to do this so that expressions (which should only occur in variable initializers and
   * initializer blocks) can know which fields have already been declared
   */
  public TypeData forUninitializedVariableDeclaratorOnly(UninitializedVariableDeclarator that, 
                                                            TypeData type_result, 
                                                            TypeData name_result) {
    Word name = that.getName();
    String text = that.getName().getText();
    VariableData vd = getFieldOrVariable(text, _symbolData, _symbolData, name);
    if (vd == null) {
      throw new RuntimeException("The field " + text + " was not found in " + _symbolData.getName() + ".");
    }
    _vars.addLast(vd);
    return null;
  }


  /**Make sure the method is not missing a return type.  
   * If expected and actual are both not null, then their relationship (i.e.
   * is actual a subclass of expected) is checked when the value return statement that returns
   * actual is processed.
   */
  private void _checkReturnType(SymbolData expected, SymbolData actual, ConcreteMethodDef that) {
    // If the return type is void, the BodyTypeChecker will make sure there are no return statements with expressions.
    if (expected == SymbolData.VOID_TYPE) {
      if (actual == null || actual == SymbolData.VOID_TYPE) {
        // correct, do nothing
      }
    }
    else {
      if (actual == null) {
        _addError("This method is missing a return statement.", that);
      }
    }
  }
  
  /**
   * Finds the corresponding MethodData for this constructor first.  Then visits the body, making sure that
   * all final fields are assigned a value and that no final fields are reassigned.
   */
  public TypeData forConstructorDef(ConstructorDef that) {
    hasConstructor = true;
    final TypeData mav_result = that.getMav().visit(this);
    final TypeData[] parameters_result = makeArrayOfRetType(that.getParameters().length);
    final TypeData[] throws_result = makeArrayOfRetType(that.getThrows().length);
    for (int i = 0; i < that.getThrows().length; i++) {
      throws_result[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that);     //that.getThrows()[i].visit(this);
    }

    // We need to match the name and params.
    // First find the correct MethodData.
    MethodData md = null;
    FormalParameter[] fParams = that.getParameters();
    String[] paramTypes = new String[fParams.length];
    for (int i = 0; i < fParams.length; i++) {
      paramTypes[i] = fParams[i].getDeclarator().getType().getName();
    }
    LinkedList<MethodData> mds = _symbolData.getMethods();
    Iterator<MethodData> iter = mds.iterator();
    while (iter.hasNext()) {
      boolean match = true;
      MethodData tempMd = iter.next();
      if (tempMd.getName().equals(LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()))) {
        // Check the params.
        VariableData[] vds = tempMd.getParams();
        if (paramTypes.length == vds.length) {
          for (int i = 0; i < paramTypes.length; i++) {
            // The parameters should be in order.  Must also check the unqualified form of the VariableData's type.
            if(!vds[i].getType().getName().equals(paramTypes[i]) &&
               !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
              match = false;
              break;
            }
          }
          if (match) {
            md = tempMd;            
            break;
          }
        }
      }
    }
    if (md == null) {
      throw new RuntimeException("The constructor " + LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()) + " was not in the class " + _symbolData.getName() + ".");
    }

    LinkedList<VariableData> ll = new LinkedList<VariableData>();// = cloneVariableDataList(_vars);
    VariableData[] vds = md.getParams();
    for (int i = 0; i<vds.length; i++) {
      ll.addLast(vds[i]);
    }
    ll.addAll(cloneVariableDataList(_vars));

    ConstructorBodyTypeChecker btc = new ConstructorBodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll, new LinkedList<Pair<SymbolData, JExpression>>());
    final TypeData body_result = that.getStatements().visit(btc);
    
    //make sure the constructor assigns a value to any uninitialized fields (i.e. final fields)
    LinkedList<VariableData> sdVars = _symbolData.getVars();
    for (int i = 0; i<sdVars.size(); i++) { //Since non-final fields are automatically given a value, this in effect just checks the final fields.
      if (!sdVars.get(i).hasValue()) {
        _addError("The final field " + sdVars.get(i).getName() + " has not been initialized.  Make sure you give it a value in this constructor", that);
        return null;
      }
    }
    
    _symbolData.decrementConstructorCount();
    
   
    if (_symbolData.getConstructorCount() > 0) {
      unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
    }
    
    return forConstructorDefOnly(that, mav_result, parameters_result, throws_result, body_result);
  }
  

  /*Basically, a no-op*/
  public TypeData forConstructorDefOnly(ConstructorDef that, TypeData mav_result, TypeData[] parameters_result, TypeData[] throws_result, TypeData body_result) {
    return forJExpressionOnly(that);
  }
  

  /*
   * Visit all of the fields of the ConcreteMethodDef, and resolve everything.  Then, find
   * the corresponding MethodData in the SymbolData's list of methods.  It must match both in name and parameter
   * types.  Keep track of what has been assigned before we visit the method body, and then visit the
   * method body.  Then, make sure the return type of the method is okay, and unassign any variables
   * for which the assignment should not be visible outside the scope of the method.
   */
  public TypeData forConcreteMethodDef(ConcreteMethodDef that) {
    final TypeData mav_result = that.getMav().visit(this);
    final TypeData[] typeParams_result = makeArrayOfRetType(that.getTypeParams().length);
    for (int i = 0; i < that.getTypeParams().length; i++) {
      typeParams_result[i] = that.getTypeParams()[i].visit(this);
    }
    final SymbolData result_result = getSymbolData(that.getResult().getName(), _symbolData, that);
    final TypeData name_result = that.getName().visit(this);
    final TypeData[] throws_result = makeArrayOfRetType(that.getThrows().length);
    for (int i = 0; i < that.getThrows().length; i++) {
      throws_result[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
    }
    // We need to match the name and params.
    // First find the correct MethodData.
    MethodData md = null;
    FormalParameter[] fParams = that.getParams();
    String[] paramTypes = new String[fParams.length];
    for (int i = 0; i < fParams.length; i++) {
      paramTypes[i] = fParams[i].getDeclarator().getType().getName();
    }
    LinkedList<MethodData> mds = _symbolData.getMethods();
    Iterator<MethodData> iter = mds.iterator();
    while (iter.hasNext()) {
      boolean match = false;
      MethodData tempMd = iter.next();
      if (tempMd.getName().equals(that.getName().getText())) {
        match = true;

        // Check the params.
        VariableData[] vds = tempMd.getParams();
        if (paramTypes.length == vds.length) {
          for (int i = 0; i < paramTypes.length; i++) {
            // The parameters should be in order.  Must also check the unqualified form of the VariableData's type.
            if(!vds[i].getType().getName().equals(paramTypes[i]) &&
               !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
              match = false;
              break;
            }
          }
          if (match) {
            md = tempMd;            
            break;
          }
        }
      }
    }
    if (md == null) {
      throw new RuntimeException("Internal Program Error: The method " + that.getName().getText() + " was not in the class " + _symbolData.getName() + ".  Please report this bug.");
    }
    
    LinkedList<VariableData> ll = new LinkedList<VariableData>();
    VariableData[] vds = md.getParams();
    for (int i = 0; i<vds.length; i++) {
      ll.addLast(vds[i]);
    }
    ll.addAll(cloneVariableDataList(_vars));
    
    LinkedList<VariableData> thingsWeAssigned = new LinkedList<VariableData>();
    for (int i = 0; i<_symbolData.getVars().size(); i++) {
      VariableData tempVd = _symbolData.getVars().get(i);
      if (tempVd.gotValue()) { //then this variable did not have a value previously.
        thingsWeAssigned.addLast(tempVd);
      }
    }
    
    BodyTypeChecker btc = new BodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll, new LinkedList<Pair<SymbolData, JExpression>>());
    
    TypeData body_result = that.getBody().visit(btc); // We assume that this will return an InstanceData -- the return type of the body
    
    // This checks to see that the method returns the correct type.  It throws its own errors.
    if (body_result != null) {body_result = body_result.getSymbolData();}
    _checkReturnType(md.getReturnType(), (SymbolData) body_result, that);
    if (md.getReturnType() != null) {
      // Ensure that this method doesn't override another method with a different return type.
      SymbolData.checkDifferentReturnTypes(md, _symbolData, _targetVersion);
    }
    
    // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
    final TypeData[] params_result = makeArrayOfRetType(that.getParams().length);

    for (int i = 0; i<thingsWeAssigned.size(); i++) {
      thingsWeAssigned.get(i).lostValue();
    }
    
    return result_result;

  }

  
  /*
   * Make sure that this method does not override another method with a different return type,
   * since this is not allowed in java.
   */
  public TypeData forAbstractMethodDef(AbstractMethodDef that) {
    final TypeData mav_result = that.getMav().visit(this);
    final TypeData[] typeParams_result = makeArrayOfRetType(that.getTypeParams().length);
    for (int i = 0; i < that.getTypeParams().length; i++) {
      typeParams_result[i] = that.getTypeParams()[i].visit(this);
    }
    final SymbolData result_result = getSymbolData(that.getResult().getName(), _symbolData, that);
    final TypeData name_result = that.getName().visit(this);

    // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
    final TypeData[] params_result = makeArrayOfRetType(that.getParams().length);
    for (int i = 0; i<params_result.length; i++) {
      params_result[i] = getSymbolData(that.getParams()[i].getDeclarator().getType().getName(), _symbolData, that.getParams()[i]);
    }
    final TypeData[] throws_result = makeArrayOfRetType(that.getThrows().length);
    for (int i = 0; i < that.getThrows().length; i++) {
      throws_result[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
    }
    // Ensure that this method doesn't override another method with a different return type.
    MethodData md = _symbolData.getMethod(that.getName().getText(), params_result);
    if (md == null) {
      throw new RuntimeException("Internal Program Error: Could not find the method " + that.getName().getText() + " in class " + _symbolData.getName() +".  Please report this bug.");
    }
    SymbolData.checkDifferentReturnTypes(md, _symbolData, _targetVersion);

    return result_result;
  }
  
  /*Try to resolve the type, and then make sure it can be seen from where we are. */
  public TypeData forTypeOnly(Type that) {
    Data sd = getSymbolData(that.getName(), _symbolData, that);
    if (sd != null) {sd = sd.getOuterData();}
    while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
      if (!checkAccessibility(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _symbolData, "class or interface")) {
        return null;
      }
      sd = sd.getOuterData();
    }
    return forJExpressionOnly(that);
  }
  
   /**
    * Test the methods that are defined above.
    */
  public static class ClassBodyTypeCheckerTest extends TestCase {
    
    private ClassBodyTypeChecker _cbbtc;
    
    private SymbolData _sd1;
    private SymbolData _sd2;
    private SymbolData _sd3;
    private SymbolData _sd4;
    private SymbolData _sd5;
    private SymbolData _sd6;
    private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"public"});
    private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"protected"});
    private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"private"});
    private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[0]);
    private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"abstract"});
    private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"final"});
    
    
    public ClassBodyTypeCheckerTest() {
      this("");
    }
    public ClassBodyTypeCheckerTest(String name) {
      super(name);
    }
    
    public void setUp() {
      _sd1 = new SymbolData("i.like.monkey");
      _sd2 = new SymbolData("i.like.giraffe");
      _sd3 = new SymbolData("zebra");
      _sd4 = new SymbolData("u.like.emu");
      _sd5 = new SymbolData("");
      _sd6 = new SymbolData("cebu");
      errors = new LinkedList<Pair<String, JExpressionIF>>();
      symbolTable = new Symboltable();
      _cbbtc = new ClassBodyTypeChecker(_sd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
      _cbbtc._targetVersion = JavaVersion.JAVA_5;
      _cbbtc._importedPackages.addFirst("java.lang");
    }
    
    public void testForUninitializedVariableDeclaratorOnly() {
      VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
      _sd1.addVar(vd1);
      UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, 
                                                                                new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                                                                new Word(JExprParser.NO_SOURCE_INFO, "Mojo"));
//      uvd.visit(_cbbtc);
      _cbbtc.forUninitializedVariableDeclaratorOnly(uvd, SymbolData.INT_TYPE, null);
      assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));      
    }
    
    public void testForInitializedVariableDeclaratorOnly() {
      VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
      _sd1.addVar(vd1);
      InitializedVariableDeclarator ivd = new InitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, 
                                                                            new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                                                            new Word(JExprParser.NO_SOURCE_INFO, "Mojo"), 
                                                                            new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1));
      ivd.visit(_cbbtc);
      assertEquals("There should be no errors.", 0, errors.size());
      assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));
      ivd = new InitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, 
                                              new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                              new Word(JExprParser.NO_SOURCE_INFO, "Santa's Little Helper"), 
                                              new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1));
      try {
        ivd.visit(_cbbtc);
        fail("Should have thrown a RuntimeException because there's no field named Santa's Little Helper.");
      }
      catch (RuntimeException re) {
        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());
      }
    }
    
    public void test_checkReturnType() {
      /* Check tests that should work correctly first. */
      _cbbtc._checkReturnType(SymbolData.VOID_TYPE, null, null);
      _cbbtc._checkReturnType(SymbolData.VOID_TYPE, SymbolData.VOID_TYPE, null);
      _cbbtc._checkReturnType(SymbolData.INT_TYPE, SymbolData.INT_TYPE, null);
      _cbbtc._checkReturnType(SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE, null);
      
      assertEquals("There should be no errors.", 0, errors.size());
      
      /* Check tests that should each throw an error. */
      _cbbtc._checkReturnType(SymbolData.INT_TYPE, null, null);
      assertEquals("There should now be 1 error", 1, errors.size());
      assertEquals("The error message should be correct", "This method is missing a return statement.", errors.get(0).getFirst());
      
    }
    
    public void testForConcreteMethodDef() {
      FormalParameter[] fps = new FormalParameter[] {
        new FormalParameter(JExprParser.NO_SOURCE_INFO, 
                            new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, 
                                                              new PrimitiveType(JExprParser.NO_SOURCE_INFO, "double"), 
                                                              new Word (JExprParser.NO_SOURCE_INFO, "field1")),
                            false),
        new FormalParameter(JExprParser.NO_SOURCE_INFO, 
                            new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, 
                                                              new PrimitiveType(JExprParser.NO_SOURCE_INFO, "boolean"), 
                                                              new Word (JExprParser.NO_SOURCE_INFO, "field2")),
                            false)};
      ConcreteMethodDef cmd = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, 
                                                    _packageMav, 
                                                    new TypeParameter[0], 
                                                    new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                                    new Word(JExprParser.NO_SOURCE_INFO, "methodName"),
                                                    fps,
                                                    new ReferenceType[0], 
                                                    new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {
        new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5) )}));
      MethodData md = new MethodData("methodName", 
                                     _packageMav, 
                                     new TypeParameter[0], 
                                     SymbolData.INT_TYPE, 
                                     new VariableData[] { new VariableData(SymbolData.DOUBLE_TYPE), new VariableData(SymbolData.BOOLEAN_TYPE) },
                                     new String[0],
                                     _sd1,
                                     null); // no SourceInfo
      _sd1.addMethod(md);
      cmd.visit(_cbbtc);
      assertEquals("There should be no errors.", 0, errors.size());
      
      cmd = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, 
                                                    _packageMav, 
                                                    new TypeParameter[0], 
                                                    new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                                    new Word(JExprParser.NO_SOURCE_INFO, "Selma"),
                                                    fps,
                                                    new ReferenceType[0], 
                                                    new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {
                                                          new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, 
                                                                                   new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5))}));
      
      try {
        cmd.visit(_cbbtc);
        fail("Should have thrown a RuntimeException because there's no method named Selma.");
      }
      catch (RuntimeException re) {
        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());
      }
      
      
      //Check that an uninitialized variable is caught:
      PrimitiveType intt = new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int");
      UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, intt, new Word(JExprParser.NO_SOURCE_INFO, "i"));

      Statement s = new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")));
      ConcreteMethodDef cmd0 = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], intt,
                                                    new Word(JExprParser.NO_SOURCE_INFO, "invalidMethod"), new FormalParameter[0],
                                                    new ReferenceType[0], new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), s}));
      VariableData vd = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
      MethodData md0 = new MethodData("invalidMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
                                     new VariableData[0], new String[0], _sd1, cmd0);
      _sd1.addMethod(md0);
      vd.setEnclosingData(md0);
      md0.addVar(vd);
      
      _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
      _cbbtc._targetVersion= JavaVersion.JAVA_5;
      cmd0.visit(_cbbtc);
      assertEquals("There should be 1 error", 1, errors.size());
      assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", errors.get(0).getFirst());


      
      //Check that the lexical scope of an if then statement is handled correctly.
      Expression te = new LessThanExpression(JExprParser.NO_SOURCE_INFO,
                                             new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")),
                                             new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5));
      Statement ts = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 10)));
      IfThenStatement ift = new IfThenStatement(JExprParser.NO_SOURCE_INFO, te, ts);
      
      
      FormalParameter param = new FormalParameter(JExprParser.NO_SOURCE_INFO, new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, intt, new Word(JExprParser.NO_SOURCE_INFO, "j")), false);
      BracedBody bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift,
        new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")))});
      
      ConcreteMethodDef cmd1 = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], 
                                   intt, new Word(JExprParser.NO_SOURCE_INFO, "myMethod"), new FormalParameter[] {param}, 
                                                     new ReferenceType[0], bb);

      VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
      VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
      MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
                                     new VariableData[] {vd1}, new String[0], _sd1, cmd1);
      _sd1.addMethod(md1);
      vd1.setEnclosingData(md1);
      vd2.setEnclosingData(md1);
      md1.addVar(vd1);
      md1.addVar(vd2);
      
      _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
      cmd1.visit(_cbbtc);
      
      assertEquals("There should be 2 errors", 2, errors.size());
      assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", errors.get(1).getFirst());
      
      //Check that a final variable cannot be reassigned to
      s = new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")));
      VariableDeclaration i = new VariableDeclaration(JExprParser.NO_SOURCE_INFO,  _packageMav, new UninitializedVariableDeclarator[]{uvd});
      ExpressionStatement se = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 2)));
      ExpressionStatement se2 = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5)));
      
      BracedBody b = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {i, se, se2, s});
      ConcreteMethodDef cmd2 = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], intt,
                                                    new Word(JExprParser.NO_SOURCE_INFO, "doubleAssignmentMethod"), new FormalParameter[0],
                                                    new ReferenceType[0], b);
      
      VariableData vdi = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, null);
      MethodData md2 = new MethodData("doubleAssignmentMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
                                     new VariableData[0], new String[0], _sd1, cmd2);
      _sd1.addMethod(md2);
      vdi.setEnclosingData(md2);

      md2.addVar(vdi);

      _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
      cmd2.visit(_cbbtc);
      assertEquals("There should be 3 errors", 3, errors.size());
      assertEquals("The error message should be correct", "You cannot assign a value to i because it is immutable and has already been given a value", errors.get(2).getFirst());
 
       
      //test that if a variable is assigned in a branch of the if, and then returned, it is okay.
      te = new LessThanExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")),
       new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5));
      Statement assignStatement = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 10)));
      Statement returnStatement = new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")));
      ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement, returnStatement}));
      ift = new IfThenStatement(JExprParser.NO_SOURCE_INFO, te, ts);
      
      bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO,  _packageMav, new UninitializedVariableDeclarator[]{uvd}), 
        ift, 
        new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, 
                                 new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5))});
      
      ConcreteMethodDef cmd4 = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], 
                                   intt, new Word(JExprParser.NO_SOURCE_INFO, "myMethod3"), new FormalParameter[] {param}, 
                                   new ReferenceType[0], bb);

      vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
      vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
      md1 = new MethodData("myMethod3", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
                           new VariableData[] {vd1}, new String[0], _sd1, cmd4);
      md1.addVar(vd1);
      md1.addVar(vd2);
      
      vd1.setEnclosingData(md1);
      vd2.setEnclosingData(md1);

      
      _sd1.addMethod(md1);
      _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
      

      md1.addBlock(new BlockData(md1));
      cmd4.visit(_cbbtc);
      assertEquals("There should be 3 errors", 3, errors.size());
    }
    
    public void testCheckDifferentReturnTypes() {
      SymbolData superSd = new SymbolData("aiya");
      _sd1.setSuperClass(superSd);
      MethodData md3 = new MethodData("methodName",
                                      _publicMav,
                                      new TypeParameter[0],
                                      SymbolData.CHAR_TYPE,
                                      new VariableData[0],
                                      new String[0],
                                      null,
                                      null);
      superSd.addMethod(md3);
      MethodData md4 = new MethodData("methodName",
                                     _publicMav,
                                     new TypeParameter[0],
                                     SymbolData.INT_TYPE,
                                     new VariableData[0],
                                     new String[0],
                                     superSd,
                                     null);
      MethodDef mDef = new ConcreteMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                             new Word(JExprParser.NO_SOURCE_INFO, "methodName"), new FormalParameter[0], new ReferenceType[0], 
                                             new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 76))}));
      _sd1.addMethod(md4);
      _cbbtc._symbolData = _sd1;
      mDef.visit(_cbbtc);
      assertEquals("There should be one error.", 1, errors.size());
      assertEquals("There error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
                   errors.get(0).getFirst());
      mDef = new AbstractMethodDef(JExprParser.NO_SOURCE_INFO, _publicMav, new TypeParameter[0], new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), 
                                   new Word(JExprParser.NO_SOURCE_INFO, "methodName"), new FormalParameter[0], new ReferenceType[0]);
      
      mDef.visit(_cbbtc);
      assertEquals("There should be two errors.", 2, errors.size());
      assertEquals("There error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
                   errors.get(1).getFirst());
    }
    
    public void testForTypeOnly() {
      Type t = new PrimitiveType(JExprParser.NO_SOURCE_INFO, "double");
      t.visit(_cbbtc);
      assertEquals("There should be no errors", 0, errors.size());
      
      SymbolData sd = new SymbolData("Adam");
      sd.setIsContinuation(false);
      symbolTable.put("Adam", sd);
      sd.setMav(_publicMav);
      t = new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "Adam", new Type[0]);
      t.visit(_cbbtc);
      assertEquals("There should still be no errors", 0, errors.size());
      
      SymbolData innerSd = new SymbolData("Adam$Wulf");
      innerSd.setIsContinuation(false);
      sd.addInnerClass(innerSd);
      innerSd.setOuterData(sd);
      innerSd.setMav(_publicMav);
      _cbbtc.symbolTable.put("USaigehgihdsgslghdlighs", innerSd);
      t = new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "Adam.Wulf", new Type[0]);
      t.visit(_cbbtc);
      assertEquals("There should still be no errors", 0, errors.size());
      
      innerSd.setMav(_privateMav);
      t = new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "Adam.Wulf", new Type[0]);
      t.visit(_cbbtc);
      
      assertEquals("There should be one error", 1, errors.size());
      assertEquals("The error message should be correct", "The class or interface Adam.Wulf is private and cannot be accessed from " + _cbbtc._symbolData.getName(),
                   errors.get(0).getFirst());
      
      sd.setMav(_privateMav);
      innerSd.setMav(_publicMav);
      t = new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "Adam.Wulf", new Type[0]);
      t.visit(_cbbtc);
      assertEquals("There should be two errors", 2, errors.size());
      assertEquals("The error message should be correct", "The class or interface Adam is private and cannot be accessed from " + _cbbtc._symbolData.getName(),
                   errors.get(1).getFirst());
    }
    
    public void testForConstructorDef() {
      VariableDeclaration vd = new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _finalMav, new VariableDeclarator[] {new UninitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), new Word(JExprParser.NO_SOURCE_INFO, "i"))});
      ExpressionStatement se = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1)));      
      BracedBody cbb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {se});
      ConstructorDef cd = new ConstructorDef(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "Jimes"), _publicMav, new FormalParameter[0], new ReferenceType[0], cbb);
      BracedBody b = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {vd, cd});
      ClassDef classDef = new ClassDef(JExprParser.NO_SOURCE_INFO, _publicMav, new Word(JExprParser.NO_SOURCE_INFO, "Jimes"), new TypeParameter[0], new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "java.lang.Object", new Type[0]), new ReferenceType[0], b);

      SymbolData sd = new SymbolData("Jimes");
      VariableData vData = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, sd);
      _cbbtc._file = new File("Jimes.dj0");
      sd.setMav(_publicMav);
      sd.setIsContinuation(false);
      sd.addVar(vData);
      SymbolData sd2 = new SymbolData("java.lang.Object");
      sd2.setIsContinuation(false);
      sd2.setMav(_publicMav);
      sd2.setPackage("java.lang");
      sd.setSuperClass(sd2);
      symbolTable.put("Jimes", sd);
      symbolTable.put("java.lang.Object", sd2);
      MethodData md = new MethodData("Jimes", _publicMav, new TypeParameter[0], sd, new VariableData[0], new String[0], sd, cd);
      MethodData objMd = new MethodData("Object", _publicMav, new TypeParameter[0], sd2, new VariableData[0], new String[0], sd2, cd);
      sd.addMethod(md);
      
      
      //assumes an explicit super call with no arguments
      classDef.visit(_cbbtc);
      assertEquals("There should be one error", 1, errors.size());
      assertEquals("Error message should be correct", "You must invoke one of java.lang.Object's constructors here.  You can either explicitly invoke one of its exisitng constructors or add a constructor with signature: Object().", errors.getLast().getFirst());

      // test that a constructor can set the value of a final field
      sd2.addMethod(objMd); //give super class constructor
      vData.lostValue();

      classDef.visit(_cbbtc);
      assertEquals("There should still be one error", 1, errors.size());

      
      // test that if the constructor does not assign a value to the final field, then an error is thrown
      vData.lostValue();
      cbb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {});
      cd = new ConstructorDef(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "Jimes"), _publicMav, new FormalParameter[0], new ReferenceType[0], cbb);
      b = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {vd, cd});
      classDef = new ClassDef(JExprParser.NO_SOURCE_INFO, _publicMav, new Word(JExprParser.NO_SOURCE_INFO, "Jimes"), new TypeParameter[0], new ClassOrInterfaceType(JExprParser.NO_SOURCE_INFO, "java.lang.Object", new Type[0]), new ReferenceType[0], b);
 
      classDef.visit(_cbbtc);
      assertEquals("There should be 2 errors now", 2, errors.size());
      assertEquals("The error message should be correct", "The final field i has not been initialized.  Make sure you give it a value in this constructor", errors.getLast().getFirst());
      
      //test the case of a constructor that makes a call to another constructor
      vData = new VariableData("j", _finalMav, SymbolData.INT_TYPE, false, sd);
      sd.setVars(new LinkedList<VariableData>());
      sd.addVar(vData);

      LinkedList<VariableData> vs = new LinkedList<VariableData>();
      vs.addLast(vData);
      _cbbtc = new ClassBodyTypeChecker(sd, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), vs, new LinkedList<Pair<SymbolData, JExpression>>());
      ExpressionStatement assign = new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 45)));
      b = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new SimpleThisConstructorInvocation(JExprParser.NO_SOURCE_INFO, new ParenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[0]))), assign});
      cd = new ConstructorDef(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "name"), _publicMav, new FormalParameter[0], new ReferenceType[0], b);
      cd.visit(_cbbtc);
      assertEquals("There should now be 3 errors", 3, errors.size());
      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());


    }
    
  }
}



See more files for this project here

DrJava

DrJava is a lightweight programming environment for Java designed to foster test-driven software development. It includes an intelligent program editor, an interactions pane for evaluating program text, a source level debugger, and a unit testing tool.

Project homepage: http://sourceforge.net/projects/drjava
Programming language(s): Java
License: other

  AdvancedLevelTest.java
  AdvancedVisitor.java
  ArrayData.java
  Augmentor.java
  BlockData.java
  Bob.java
  BodyBodyAdvancedVisitor.java
  BodyBodyElementaryVisitor.java
  BodyBodyIntermediateVisitor.java
  BodyData.java
  BodyTypeChecker.java
  CharConverter.java
  ClassBodyAdvancedVisitor.java
  ClassBodyElementaryVisitor.java
  ClassBodyIntermediateVisitor.java
  ClassBodyTypeChecker.java
  ConstructorBodyTypeChecker.java
  Data.java
  ElementaryLevelTest.java
  ElementaryVisitor.java
  ExpressionTypeChecker.java
  InstanceData.java
  InterfaceBodyAdvancedVisitor.java
  InterfaceBodyIntermediateVisitor.java
  InterfaceBodyTypeChecker.java
  IntermediateLevelTest.java
  IntermediateVisitor.java
  JExprParseException.java
  JExprParserTest.java
  JExpressionIFPrunableDepthFirstVisitor_void.java
  LValueTypeChecker.java
  LValueWithValueTypeChecker.java
  LanguageLevelConverter.java
  LanguageLevelVisitor.java
  MethodData.java
  PackageData.java
  Pair.java
  PrimitiveData.java
  SourceInfo.java
  SymbolData.java
  Symboltable.java
  TryCatchBodyTypeChecker.java
  TypeChecker.java
  TypeData.java
  VariableData.java
  VoidMethodsNotAllowedClassBodyTypeChecker.java
  jexpr.ast
  jexpr.jj