Code Search for Developers
 
 
  

LValueTypeChecker.java from DrJava at Krugle


Show LValueTypeChecker.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.File;
import edu.rice.cs.plt.reflect.JavaVersion;

import junit.framework.TestCase;


/**
 * Do what is necessary to type check the lhs of an assignment expression.  i.e. make sure that 
 * what is on the lhs is something that can be assigned to, and if so, return its type.
 */
public class LValueTypeChecker extends JExpressionIFAbstractVisitor<TypeData> {
  
  /**Instance of the testAssignable visitor that will be used to make sure that if the lhs
   * is something of the type that could be assigned to, it can actually be assigned to*/
  private final TestAssignable _testAssignableInstance;
  
  //The visitor that invoked this: holds the error list
  private final Bob _bob;

  /* Constructor for LValueTypeChecker.  Initializes _testAssignableInstance.
   * @param bob  The visitor that invoked this visitor.
   */
  public LValueTypeChecker(Bob bob) {
    _testAssignableInstance = new TestAssignable(bob._data, bob._file, bob._package, bob._importedFiles, bob._importedPackages, bob._vars, bob._thrown);
    _bob = bob;
  }
  
  /** Most expressions cannot appear on the lhs of an assignment: give an appropriate error */
  public TypeData defaultCase(JExpressionIF that) {
    _bob._addError("You cannot assign a value to an expression of this kind.  Values can only be assigned to fields or variables", that);
    return null;
  }
  
  /**An increment expression is a special case that cannot appear on the lhs*/
  public TypeData forIncrementExpression(IncrementExpression that) {
    _bob._addError("You cannot assign a value to an increment expression", that);
    return null;
  }
  
  /*Names can appear on the lhs, so check to see if the name can be assigned to*/
  public TypeData forNameReference(NameReference that) {
    return that.visit(_testAssignableInstance);
  }
      
  /**Array accesses can appear on the lhs, so check to see if the array can be assigned to*/
  public TypeData forArrayAccess(ArrayAccess that) {
    return that.visit(_testAssignableInstance);
  }
      
  /**Recur on the value stored in the parentheses*/
  public TypeData forParenthesized(Parenthesized that) {
    return that.getValue().visit(this);
  }
  
  /**Checks to see if what is on the lhs is assignable*/
  private class TestAssignable extends ExpressionTypeChecker {
    
    public TestAssignable(Data data, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
      super(data, file, packageName, importedFiles, importedPackages, vars, thrown);
    }

     /**
      * A SimpleName reference is one of three things:
      * 1. A variable reference, in which case the variable can be assigned to as long as it is not immutable and already has a value, or it is non-static in a static context
      * 2. A class name reference (such as String), which cannot be assigned to.
      * 3. A PackageData i.e. unresolved type which cannot be assigned to.
      */
    public TypeData forSimpleNameReference(SimpleNameReference that) {
      Word myWord = that.getName();
      myWord.visit(this);
      
      VariableData reference = getFieldOrVariable(myWord.getText(), _data, _data.getSymbolData(), that, _vars, true, true);
      if (reference != null) {
        if (!canBeAssigned(reference)) {
          _addError("You cannot assign a value to " + reference.getName() + " because it is immutable and has already been given a value", that.getName());
        }
        else if (reference.gotValue()) { _bob.thingsThatHaveBeenAssigned.add(reference);}
        
        
        //if reference is non-static, but context is static, give error
        if (!reference.hasModifier("static") && inStaticMethod()) {
          _addError("Non static field or variable " + reference.getName() + " cannot be referenced from a static context", that);
        }

        return reference.getType().getInstanceData();
      }
      
      SymbolData classR = findClassReference(null, myWord.getText(), that);
      if (classR == SymbolData.AMBIGUOUS_REFERENCE) {return null;}
      if (classR != null) {
        if (checkAccessibility(that, classR.getMav(), classR.getName(), classR, _data.getSymbolData(), "class or interface", false)) {
          return classR;
        }
      }
      
      PackageData packageD = new PackageData(myWord.getText());
      return packageD;
    }
    
    /**
     * Here is a table that explains what is allowed:
     *              result:
     * left:        package |        symbol            | instance
     * package  |     yes      yes(if class exists)         no
     * symbol   |     no       yes, if static inner class   yes if field is static and assignable
     * instance |     no       ERROR                        yes, if field is assignable
     */
    public TypeData forComplexNameReference(ComplexNameReference that) {
      ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
      TypeData lhs = that.getEnclosing().visit(etc);
      _bob.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned); //update internal Bob's list of what got assigned
      Word myWord = that.getName();
      
      //if lhs is a package data, either we found a class reference or this piece is still part of the package
      if (lhs instanceof PackageData) {
        SymbolData classRef =  findClassReference(lhs, myWord.getText(), that);
        if (classRef != null) {return classRef;}
        return new PackageData((PackageData) lhs, myWord.getText());
      }
      
      //if the word is a variable reference, make sure it can be seen from this context
      VariableData reference = getFieldOrVariable(myWord.getText(), lhs.getSymbolData(), _data.getSymbolData(), that);
      if (reference != null) {
        if (lhs instanceof SymbolData) {
          //does this reference a field? if so, it must be static
          if (!reference.hasModifier("static")) {
            _addError("Non-static variable " + Data.dollarSignsToDots(reference.getName()) + " cannot be accessed from the static context " + Data.dollarSignsToDots(lhs.getName()) + ".  Perhaps you meant to instantiate an instance of " + Data.dollarSignsToDots(lhs.getName()), that);
            return reference.getType().getInstanceData();
          }
        }
        
        //make sure it can be assigned
        if (!canBeAssigned(reference)) {
          _addError("You cannot assign a value to " + Data.dollarSignsToDots(reference.getName()) + " because it is immutable and has already been given a value", that.getName());
        }
  
        else if (reference.gotValue()) { _bob.thingsThatHaveBeenAssigned.add(reference);}

        
        return reference.getType().getInstanceData();
      }
      
      //does this reference an inner class? if so, it must be static
      SymbolData sd = getSymbolData(true, myWord.getText(), lhs.getSymbolData(), that, false);
      if (sd != null && sd != SymbolData.AMBIGUOUS_REFERENCE) {
        
        if (!checkAccessibility(that, sd.getMav(), sd.getName(), sd, _data.getSymbolData(), "class or interface")) {return null;}

        if (!sd.hasModifier("static")) {
          _addError("Non-static inner class " + Data.dollarSignsToDots(sd.getName()) + " cannot be accessed from this context.  Perhaps you meant to instantiate it", that);
        }
        
        //you cannot reference static inner classes from the context of an instantiation of their outer class
        else if (lhs instanceof InstanceData) {
          _addError("You cannot reference the static inner class " + Data.dollarSignsToDots(sd.getName()) + " from an instance of " + Data.dollarSignsToDots(lhs.getName()), that);
        }
        return sd;
      }
      
      if (sd != SymbolData.AMBIGUOUS_REFERENCE) {_addError("Could not resolve " + myWord.getText() + " from the context of " + Data.dollarSignsToDots(lhs.getName()), that);}
      return null;
    }
    
    /**
     * Type-check the lhs and the index.
     */
    public TypeData forArrayAccess(ArrayAccess that) {
      ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
      TypeData lhs = that.getArray().visit(etc);
      _bob.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned); //update internal Bob's list of what got assigned
      
      ExpressionTypeChecker indexTC = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
      TypeData index = that.getIndex().visit(indexTC);
      _bob.thingsThatHaveBeenAssigned.addAll(indexTC.thingsThatHaveBeenAssigned); //update internal Bob's list of what got assigned
      
      
      return forArrayAccessOnly(that, lhs, index);
    }
    
    /**A VariableData can be assigned to if it is not final or if it does not already have a value */
    protected boolean canBeAssigned(VariableData vd) {
      return !vd.isFinal() || !vd.hasValue();
    }
    
  }

  
  /**
   * Test the methods defined in the above class
   */
  public static class LValueTypeCheckerTest extends TestCase {
    
    private LValueTypeChecker _lvtc;
    LValueTypeChecker.TestAssignable _ta;
    
    
    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"});
    private ModifiersAndVisibility _finalPublicMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"final", "public"});
    private ModifiersAndVisibility _publicAbstractMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"public", "abstract"});
    private ModifiersAndVisibility _publicStaticMav = new ModifiersAndVisibility(JExprParser.NO_SOURCE_INFO, new String[] {"public", "static"});
    
    
    public LValueTypeCheckerTest() {
      this("");
    }
    public LValueTypeCheckerTest(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");
      _lvtc = new LValueTypeChecker(new Bob(_sd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>()));
      _ta = _lvtc._testAssignableInstance;
      _lvtc._bob.errors = new LinkedList<Pair<String, JExpressionIF>>();
      _lvtc._bob.symbolTable = new Symboltable();
      LanguageLevelVisitor.symbolTable = _lvtc._bob.symbolTable;
      _lvtc._bob._targetVersion = JavaVersion.JAVA_5;
      _lvtc._bob._importedPackages.addFirst("java.lang");
    }
    
    //Test methods of LeftValueTypeChecker:

    public void testDefaultCase() {
      //should add an error
      new NullLiteral(JExprParser.NO_SOURCE_INFO).visit(_lvtc);
      assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind.  Values can only be assigned to fields or variables",
                   _lvtc._bob.errors.getLast().getFirst());

      //should add an error
      new PlusExpression(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 21), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 22)).visit(_lvtc);
      assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind.  Values can only be assigned to fields or variables",
                   _lvtc._bob.errors.getLast().getFirst());
    }
     
    
    public void testForIncrementExpression() {
      //should add an error
      PositivePrefixIncrementExpression p = new PositivePrefixIncrementExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "bob")));
      assertEquals("Should return null", null, p.visit(_lvtc));
      assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to an increment expression", _lvtc._bob.errors.getLast().getFirst());
    }
    
    public void testForSimpleNameReference() {
      //first, consider the case where what we have is a variable reference:
      SimpleNameReference var = new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "variable1"));
      VariableData varData = new VariableData("variable1", _publicMav, SymbolData.INT_TYPE, false, _ta._data);
      _ta._vars.add(varData);
      
      //in this case, it has not been initialized--but this is okay--should not throw error
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
      assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
      
      //if it has been initialized but is not final, do not give an error
      varData.gotValue();
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
      assertEquals("Should still be 0 errors", 0, _lvtc._bob.errors.size());

      //if it has been initialized and is final, give an error
      varData.setMav(_finalMav);
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
      assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to variable1 because it is immutable and has already been given a value", _lvtc._bob.errors.getLast().getFirst());
      varData.setMav(_publicMav);
      
      //if variable is non-static, but you are in static context, cannot reference it. Should give error
      MethodData newContext = new MethodData("method", _publicStaticMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[0], new String[0], _sd1, new NullLiteral(JExprParser.NO_SOURCE_INFO)); 
      _ta._data = newContext;
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
      assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Non static field or variable variable1 cannot be referenced from a static context", _lvtc._bob.errors.getLast().getFirst());
      _ta._data = _sd1;
      
      //if it is a variable of your super class, it won't be in _vars.  Check this case.
      _ta._vars = new LinkedList<VariableData>();
      _sd1.setSuperClass(_sd2);
      _sd2.addVar(varData);
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), var.visit(_lvtc));
      assertEquals("Should still be 2 errors", 2, _lvtc._bob.errors.size());
      
      //now, consider the case where what we have is a class reference:
      SimpleNameReference className = new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "Frog"));
      SymbolData frog = new SymbolData("Frog");
      frog.setIsContinuation(false);
      _lvtc._bob.symbolTable.put("Frog", frog);
      
      //if it is not visibile from this context, return package data
      TypeData result = className.visit(_lvtc);
      assertTrue("Result should be a PackageData since Frog is not accessible", result instanceof PackageData);
      assertEquals("Should have correct name", "Frog", result.getName());
      assertEquals("Should still be 2 errors", 2, _lvtc._bob.errors.size());
      
      //if it is visibile from this context, no error
      frog.setMav(_publicMav);
      assertEquals("Should return Frog", frog, className.visit(_lvtc));
      assertEquals("Should still be 2 errors", 2, _lvtc._bob.errors.size());
      
      //If the name cannot be resolved, simply return a packageData.
      SimpleNameReference fake = new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "notRealReference"));
      assertEquals("Should return package data", "notRealReference", (fake.visit(_lvtc)).getName());
      assertEquals("Should still be just 2 errors", 2, _lvtc._bob.errors.size());
      
      //if the reference is ambiguous (matches both an interface and a class) give an error
      SimpleNameReference ambigRef = new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "ambigThing"));

      SymbolData interfaceD = new SymbolData("interface");
      interfaceD.setIsContinuation(false);
      interfaceD.setInterface(true);
      interfaceD.setMav(_publicMav);
      
      SymbolData classD = new SymbolData("superClass");
      classD.setIsContinuation(false);
      classD.setMav(_publicMav);
      
      SymbolData ambigThingI = new SymbolData("ambigThing");
      ambigThingI.setIsContinuation(false);
      ambigThingI.setInterface(true);
      interfaceD.addInnerInterface(ambigThingI);
      ambigThingI.setOuterData(interfaceD);
      ambigThingI.setMav(_publicStaticMav);
      
      SymbolData ambigThingC = new SymbolData("ambigThing");
      ambigThingC.setIsContinuation(false);
      classD.addInnerClass(ambigThingC);
      ambigThingC.setOuterData(classD);
      ambigThingC.setMav(_publicStaticMav);
      
      _sd6.addInterface(interfaceD);
      _sd6.setSuperClass(classD);
      
      _sd6.setMav(_publicMav);
      _sd6.setIsContinuation(false);
      
      _ta._data = _sd6;

      assertEquals("Should return null", null, ambigRef.visit(_lvtc));
      assertEquals("Should be 3 errors", 3, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Ambiguous reference to class or interface ambigThing", _lvtc._bob.errors.getLast().getFirst());    
    }
     
    public void testForComplexNameReference() {
      //if lhs is a package data, we want to keep building it:
      
      //if whole reference is just package reference, return package data
      ComplexNameReference ref1 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "java")), new Word(JExprParser.NO_SOURCE_INFO, "lang"));
      assertEquals("Should return correct package data", "java.lang", ref1.visit(_lvtc).getName());
      assertEquals("Should be no errors", 0, _lvtc._bob.errors.size());
      
      //if reference builds to a class in the symbol table, return that class
      ComplexNameReference ref2 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, ref1, new Word(JExprParser.NO_SOURCE_INFO, "String"));
      SymbolData string = new SymbolData("java.lang.String");
      string.setPackage("java.lang");
      string.setMav(_publicMav);
      string.setIsContinuation(false);
      _lvtc._bob.symbolTable.put("java.lang.String", string);
      
      assertEquals("Should return string", string, ref2.visit(_lvtc));

      assertEquals("Should still be no errors", 0, _lvtc._bob.errors.size());
      

      //if lhs is not a package data, it gets more complicated:
      
      //we're referencing a variable inside of symbol data lhs:
      VariableData myVar = new VariableData("myVar", _publicStaticMav, SymbolData.DOUBLE_TYPE, true, string);
      string.addVar(myVar);
      ComplexNameReference varRef1 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, ref2, new Word(JExprParser.NO_SOURCE_INFO, "myVar"));
      
      //static var from static context
      assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
      assertEquals("There should still be no errors", 0, _lvtc._bob.errors.size());
      
      //static uninitialized var from static context--okay, because we want to give it a value
      myVar.lostValue();
      assertEquals("Should return Double_Type instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
      assertEquals("There should still be no errors", 0, _lvtc._bob.errors.size());

     
      //non-static var--this is a static context
      myVar.setMav(_publicMav);
      assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef1.visit(_lvtc));
      assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Non-static variable myVar cannot be accessed from the static context java.lang.String.  Perhaps you meant to instantiate an instance of java.lang.String", _lvtc._bob.errors.getLast().getFirst());
      
      
      //non-static context, okay to reference non-static var
      VariableData stringVar = new VariableData("s", _publicMav, string, true, _lvtc._bob._data);
      _ta._vars.add(stringVar);
      ComplexNameReference varRef2 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "s")), new Word(JExprParser.NO_SOURCE_INFO, "myVar"));
      assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
      assertEquals("Should still just be 1 error", 1, _lvtc._bob.errors.size());
      
      //if it has been initialized and is final, give an error
      myVar.setMav(_finalPublicMav);
      myVar.gotValue();
      assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
      assertEquals("Should be 2 errors", 2, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to myVar because it is immutable and has already been given a value", _lvtc._bob.errors.getLast().getFirst());
      myVar.setMav(_publicMav);
      myVar.lostValue();

      //if it is a variable of the super class, you should still be able to see it.  Check this case.
      string.setVars(new LinkedList<VariableData>());
      string.setSuperClass(_sd2);
      _sd2.addVar(myVar);
      assertEquals("Should return double instance", SymbolData.DOUBLE_TYPE.getInstanceData(), varRef2.visit(_lvtc));
      assertEquals("Should still be 2 errors", 2, _lvtc._bob.errors.size());

      //here's a complex multiple variable reference case:
      VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, true, _sd1);
      VariableData vd2 = new VariableData("Santa's Little Helper", _publicMav, _sd1, true, _sd2);
      VariableData vd3 = new VariableData("Snowball1", _publicMav, _sd2, true, _sd3);
      _sd3.addVar(vd3);
      _sd2.addVar(vd2);
      _sd1.addVar(vd1);
      
      ComplexNameReference varRef3 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "Snowball1")),
                                                        new Word(JExprParser.NO_SOURCE_INFO, "Santa's Little Helper"));
      ComplexNameReference varRef4 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, varRef3, new Word(JExprParser.NO_SOURCE_INFO, "Mojo"));
      
      Data oldData = _lvtc._bob._data;
      _lvtc._bob._data = _sd3;
      _lvtc._bob._vars.add(vd3);

      TypeData result = varRef4.visit(_lvtc);
      assertEquals("Should return int instance", SymbolData.INT_TYPE.getInstanceData(), result);
      assertEquals("Should still be 2 errors", 2, _lvtc._bob.errors.size());
      

      _lvtc._bob._data = oldData;
      

      //what if what we have is an inner class?
      SymbolData inner = new SymbolData("java.lang.String$Inner");
      inner.setPackage("java.lang");
      inner.setIsContinuation(false);
      inner.setOuterData(string);
      string.addInnerClass(inner);
      
      //if inner is not visible, throw error
      ComplexNameReference innerRef0 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "s")), new Word(JExprParser.NO_SOURCE_INFO, "Inner"));
      assertEquals("Should return null", null, innerRef0.visit(_lvtc));
      assertEquals("Should be 3 errors", 3, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "The class or interface java.lang.String.Inner is package protected because there is no access specifier and cannot be accessed from i.like.monkey", _lvtc._bob.errors.getLast().getFirst());

      inner.setMav(_publicMav);
      
      //if inner is not static, give error:
      ComplexNameReference innerRef1 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, ref2, new Word(JExprParser.NO_SOURCE_INFO, "Inner"));
      assertEquals("Should return inner", inner, innerRef1.visit(_lvtc));
      assertEquals("Should be 4 errors", 4, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Non-static inner class java.lang.String.Inner cannot be accessed from this context.  Perhaps you meant to instantiate it", _lvtc._bob.errors.getLast().getFirst());
  
      //if inner is not static and outer is not static, it's okay...
      ComplexNameReference innerRef2 = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "s")), new Word(JExprParser.NO_SOURCE_INFO, "Inner"));
      assertEquals("Should return inner", inner, innerRef2.visit(_lvtc));
      assertEquals("Should be 5 errors", 5, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Non-static inner class java.lang.String.Inner cannot be accessed from this context.  Perhaps you meant to instantiate it", _lvtc._bob.errors.getLast().getFirst());

      //if inner is static and outer is not static, throw error
      inner.setMav(_publicStaticMav);
      assertEquals("Should return inner", inner, innerRef2.visit(_lvtc));
      assertEquals("Should be 6 errors", 6, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot reference the static inner class java.lang.String.Inner from an instance of java.lang.String", _lvtc._bob.errors.getLast().getFirst());
      
      
      //if the symbol could not be matched, give an error and return null
      ComplexNameReference noSense = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, ref2, new Word(JExprParser.NO_SOURCE_INFO, "nonsense"));
      assertEquals("Should return null", null, noSense.visit(_lvtc));
      assertEquals("Should be 7 errors", 7, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Could not resolve nonsense from the context of java.lang.String", _lvtc._bob.errors.getLast().getFirst());

      //if the reference is ambiguous (matches both an interface and a class) give an error
      ComplexNameReference ambigRef = new ComplexNameReference(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "cebu")), new Word(JExprParser.NO_SOURCE_INFO, "ambigThing"));

      SymbolData interfaceD = new SymbolData("interface");
      interfaceD.setIsContinuation(false);
      interfaceD.setInterface(true);
      interfaceD.setMav(_publicMav);
      
      SymbolData classD = new SymbolData("superClass");
      classD.setIsContinuation(false);
      classD.setMav(_publicMav);
      
      SymbolData ambigThingI = new SymbolData("ambigThing");
      ambigThingI.setIsContinuation(false);
      ambigThingI.setInterface(true);
      interfaceD.addInnerInterface(ambigThingI);
      ambigThingI.setOuterData(interfaceD);
      ambigThingI.setMav(_publicStaticMav);
      
      SymbolData ambigThingC = new SymbolData("ambigThing");
      ambigThingC.setIsContinuation(false);
      classD.addInnerClass(ambigThingC);
      ambigThingC.setOuterData(classD);
      ambigThingC.setMav(_publicStaticMav);
      
      _sd6.addInterface(interfaceD);
      _sd6.setSuperClass(classD);
      
       _lvtc._bob.symbolTable.put("cebu", _sd6);
      _sd6.setMav(_publicMav);
      _sd6.setIsContinuation(false);

      assertEquals("Should return null", null, ambigRef.visit(_lvtc));
      assertEquals("Should be 8 errors", 8, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "Ambiguous reference to class or interface ambigThing", _lvtc._bob.errors.getLast().getFirst());    
      

    }
    
    public void testForArrayAccess() {
      ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, 
                                   new LanguageLevelVisitor(_lvtc._bob._file, _lvtc._bob._package, _lvtc._bob._importedFiles, _lvtc._bob._importedPackages, new LinkedList<String>(), new Hashtable<String, Pair<TypeDefBase, LanguageLevelVisitor>>(), new Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>>()),
                                   JExprParser.NO_SOURCE_INFO);
      VariableData variable1 = new VariableData("variable1", _publicMav, intArray, true, _ta._data);
      _ta._vars.add(variable1);
      
      VariableData intVar = new VariableData("intVar", _publicMav, SymbolData.INT_TYPE, true, _ta._data);
      _ta._vars.add(intVar);

      MethodData makeArray = new MethodData("makeArray", _privateMav, new TypeParameter[0], intArray, new VariableData[0], new String[0], _ta._data.getSymbolData(), new NullLiteral(JExprParser.NO_SOURCE_INFO));
      _ta._data.getSymbolData().addMethod(makeArray);

      //first, a simple index into an int[]
      
      ArrayAccess a1 = new ArrayAccess(JExprParser.NO_SOURCE_INFO, 
                                       new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "variable1")),
                                       new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "intVar")));

      assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a1.visit(_lvtc));
      assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
      
      
      //make sure that an arbitrary expression can occur in the index
      
      ArrayAccess a2 = new ArrayAccess(JExprParser.NO_SOURCE_INFO,
                                       new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "variable1")),
                                       new PlusExpression(JExprParser.NO_SOURCE_INFO,
                                                          new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 12),
                                                          new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 22)));
                                                               
      assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a2.visit(_lvtc));
      assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
      
      //make sure that an arbitrary expression can occur in the array
      
      ArrayAccess a3 = new ArrayAccess(JExprParser.NO_SOURCE_INFO,
                                       new SimpleMethodInvocation(JExprParser.NO_SOURCE_INFO,
                                                                  new Word(JExprParser.NO_SOURCE_INFO, "makeArray"),
                                                                  new ParenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[0])),
                                       new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "intVar")));
      assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), a3.visit(_lvtc));
      assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
    }
      
    public void testForParenthesized() {
      
      VariableData x = new VariableData("x", _publicMav, SymbolData.INT_TYPE, false, _ta._data);
      _ta._vars.add(x);
      
      // make sure (((x))) is okay
      Parenthesized p1 = new Parenthesized(JExprParser.NO_SOURCE_INFO,
                                           new Parenthesized(JExprParser.NO_SOURCE_INFO,
                                                             new Parenthesized(JExprParser.NO_SOURCE_INFO,
                                                                              new SimpleNameReference(JExprParser.NO_SOURCE_INFO,
                                                                                                      new Word(JExprParser.NO_SOURCE_INFO, "x")))));
      
      assertEquals("should return int", SymbolData.INT_TYPE.getInstanceData(), p1.visit(_lvtc));
      assertEquals("Should be 0 errors", 0, _lvtc._bob.errors.size());
      
      // make sure ((1)) breaks
      Parenthesized p2 = new Parenthesized(JExprParser.NO_SOURCE_INFO,
                                           new Parenthesized(JExprParser.NO_SOURCE_INFO,
                                                             new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1)));
      assertEquals("should return null", null, p2.visit(_lvtc));
      assertEquals("Should be 1 error", 1, _lvtc._bob.errors.size());
      assertEquals("Error message should be correct", "You cannot assign a value to an expression of this kind.  Values can only be assigned to fields or variables", 
                   _lvtc._bob.errors.getLast().getFirst());
      
    }
    
    
    
    //methods of TestAssignable are implicitly tested above.
    
  }
  
}




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