BodyTypeChecker.java from DrJava at Krugle
Show BodyTypeChecker.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;
/*TypeChecks the context of a body, such as a method body. Common to all Language Levels.*/
public class BodyTypeChecker extends Bob {
/**
* The MethodData of this method.
*/
protected BodyData _bodyData;
/* Constructor for BodyTypeChecker. Calls the super constructor for everything except bodyData, which we store a copy of
* in this visitor in order to have the proper type at compile time. (Bob stores it as a Data).
* @param bodyData The enclosing BodyData for the context we are type checking.
* @param file The File corresponding to the source file.
* @param packageName The package name from the source file.
* @param importedFiles The names of the files that are specifically imported (through a class import statement) in the source file.
* @param importedPackages The names of all the packages that are imported through a package import statement in the source file.
* @param vars The list of VariableData that have already been defined (used so we can make sure we don't use a variable before it has been defined).
* @param thrown The list of exceptions thrown in this body
*/
public BodyTypeChecker(BodyData bodyData, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
super(bodyData, file, packageName, importedFiles, importedPackages, vars, thrown);
_bodyData = bodyData;
}
/*@return the bodyData (enclosing data) for this context.*/
protected Data _getData() {
return _bodyData;
}
/*@return the instance data of the class/interface enclosing this body data*/
public TypeData forSimpleThisReferenceOnly(SimpleThisReference that) {
return _bodyData.getSymbolData().getInstanceData();
}
/*@return the instance data of the super class of the class enclosing this body data*/
public TypeData forSimpleSuperReferenceOnly(SimpleSuperReference that) {
return _bodyData.getSymbolData().getSuperClass().getInstanceData();
}
/**
* Create a new instance of this class for visiting inner bodies.
*/
protected BodyTypeChecker createANewInstanceOfMe(BodyData bodyData, File file, String pakage, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
return new BodyTypeChecker(bodyData, file, pakage, importedFiles, importedPackages, vars, thrown);
}
/* There is currently no way to differentiate between a block statement and
* an instance initializer in a braced body given the general nature of a
* braced body. Whenever an instance initialization is visited in a method
* body, we must assume that it is a block statement.
*/
public TypeData forInstanceInitializer(InstanceInitializer that) {
return forBlock(that.getCode());
}
/**
* 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. Add all the variable
* datas that are declared in this declarator to the list of variables that are visibile from where we are.
*/
public TypeData forUninitializedVariableDeclaratorOnly(UninitializedVariableDeclarator that,
TypeData type_result,
TypeData name_result) {
_vars.addLast(_bodyData.getVar(that.getName().getText()));
return null;
}
/** Look at the result of each item in the body. If one is not null and does not correspond to an Expression
* Statement, then that means that that statement returns a value. Check to make sure that there are no
* statements following it. If there are, then those statements are unreachable so give an error.
* @param that The Body we were type checking
* @param items_result Array of results for each item in the body that was visited.
*/
public TypeData forBodyOnly(Body that, TypeData[] items_result) {
for (int i = 0; i < items_result.length; i++) {
if (items_result[i] != null && !(that.getStatements()[i] instanceof ExpressionStatement)) {
// Found a statement that returns a value.
if (i < items_result.length - 1) {
// there must be unreachable statements
_addError("Unreachable statement.", (JExpression)that.getStatements()[i+1]);
}
// either way, return the result to keep on type-checking.
return items_result[i];
}
}
return null;
}
/*Delegate to forBodyOnly*/
public TypeData forBracedBodyOnly(BracedBody that, TypeData[] items_result) {
return forBodyOnly(that, items_result);
}
/*Delegate to forBodyOnly*/
public TypeData forUnbracedBodyOnly(UnbracedBody that, TypeData[] items_result) {
return forBodyOnly(that, items_result);
}
/*Make sure the enclosing method data is declared to return void. If it is not, give an error.
*@return the type the method is declared to return.
*/
public TypeData forVoidReturnStatementOnly(VoidReturnStatement that) {
MethodData md = _bodyData.getMethodData();
if (md.getReturnType() != SymbolData.VOID_TYPE) {
_addError("Cannot return void when the method's expected return type is not void.", that);
// Return the correct type to allow type-checking to continue.
return md.getReturnType().getInstanceData();
}
return SymbolData.VOID_TYPE.getInstanceData();
}
/*
* Visit the value being returned to determine its type. Do the necessary bookkeeping and
* then delegate to forValueReturnStatementOnly.
*/
public TypeData forValueReturnStatement(ValueReturnStatement that) {
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
TypeData value_result = that.getValue().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
return forValueReturnStatementOnly(that, value_result);
}
/*
* Make sure that the enclosing method is declared to throw the same type as the return statement
* is trying to return. Also make sure that what is being returned is an instance of the type,
* not the type itself.
*/
public TypeData forValueReturnStatementOnly(ValueReturnStatement that, TypeData value_result) {
MethodData md = _bodyData.getMethodData();
SymbolData expected = md.getReturnType();
if (expected == null) {
// There was an error processing the method's return type; return the result type
return value_result;
}
if (value_result == null || !assertFound(value_result, that)) {
// There was an error parsing the return type, return the expected type.
return expected.getInstanceData();
}
if (value_result != null && !value_result.isInstanceType()) {
_addError("You cannot return a class or interface name. Perhaps you meant to say " + value_result.getName() + ".class or to create an instance", that);
return value_result.getInstanceData();
}
if (expected == SymbolData.VOID_TYPE) {
_addError("Cannot return a value when the method's expected return type is void.", that);
// Return the correc type to allow type-checking to continue.
return SymbolData.VOID_TYPE.getInstanceData();
}
else if (!_isAssignableFrom(expected, value_result.getSymbolData())) {
_addError("This method expected to return type: \"" +
expected.getName() + "\" but here returned type: \"" + value_result.getName() + "\".", that);
}
return value_result;
}
/*
* First, visit the condition expression of the for statement with a special visitor that
* makes sure no assignment is done.
* Then, visit the condition expression with the ExpressionTypeChecker which will do all the
* normal expression stuff.
* Then, visit the update and and code (block) of the for statement with this visitor.
* Be very careful about maintaing the various scopes here.
*/
public TypeData forForStatement(ForStatement that) {
Boolean expOk = Boolean.TRUE;
if (that.getCondition() instanceof Expression) {
Expression exp = (Expression) that.getCondition();
// Assignment cannot be used in this expression
expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of a for-statement"));
}
BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData init_result = that.getInit().visit(btc);
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, btc._vars, _thrown);
final TypeData condition_result = that.getCondition().visit(etc);
btc.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
final TypeData update_result = that.getUpdate().visit(btc);
final TypeData code_result = that.getCode().visit(btc);
//Now, change any VariableDatas that were given a value in the ForStatement back to having been unassigned since its code is not
//necessarily executed.
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
if (expOk.booleanValue()) {return forForStatementOnly(that, init_result, condition_result, update_result, code_result);}
else {return null;}
}
/* Make sure that the conditional expression has the right type. */
public TypeData forForStatementOnly(ForStatement that, TypeData init_result, TypeData condition_result, TypeData update_result, TypeData code_result) {
if (condition_result != null && assertFound(condition_result, that)) {
if (!condition_result.isInstanceType()) {
_addError("This for-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that);
}
else if (!condition_result.getSymbolData().isBooleanType(_targetVersion)) {
_addError("This for-statement's conditional expression must be a boolean value. Instead, its type is " + condition_result.getName(), that);
}
}
return null;
}
/*
* First, visit the condition expression of the if statement with a special visitor that
* makes sure no assignment is done.
* Then, visit the condition expression with the ExpressionTypeChecker which will do all the
* normal expression stuff.
* Then, visit the body of the if statement with this visitor.
* Be very careful about maintaing the various scopes here.
*/
public TypeData forIfThenStatement(IfThenStatement that) {
Boolean expOk = Boolean.TRUE;
if (that.getTestExpression() instanceof Expression) {
Expression exp = (Expression) that.getTestExpression();
// Assignment cannot be used in this expression
expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of an if-then statement"));
}
//Update what has been assigned here with results from test expression, because any variables it sees or assigns will be visible
//in the rest of the body.
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData testExpression_result = that.getTestExpression().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
//Use a new visitor for the body of the then statement--it is a different lexical scope, so we want to track the
//variables seperately.
BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData thenStatement_result = that.getThenStatement().visit(btc);
//Now, change any VariableDatas that were given a value in the ThenStatement back to having been unassigned
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
if (expOk.booleanValue()) {return forIfThenStatementOnly(that, testExpression_result, thenStatement_result);}
return null;
}
/* Make sure that the conditional expression has the right type. */
public TypeData forIfThenStatementOnly(IfThenStatement that, TypeData testExpression_result, TypeData thenStatement_result) {
if (testExpression_result != null && assertFound(testExpression_result, that.getTestExpression())) {
if (!testExpression_result.isInstanceType()) {
_addError("This if-then-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that);
}
else if (!testExpression_result.getSymbolData().isBooleanType(_targetVersion)) {
_addError("This if-then-statement's conditional expression must be a boolean value. Instead, its type is " + testExpression_result.getName(), that.getTestExpression());
}
}
return null;
}
/*
* First, visit the condition expression of the if-then-else statement with a special visitor that
* makes sure no assignment is done.
* Then, visit the condition expression with the ExpressionTypeChecker which will do all the
* normal expression stuff.
* Then, visit the body of the if statement and the else with this visitor.
* Be very careful about maintaing the various scopes here.
*/
public TypeData forIfThenElseStatement(IfThenElseStatement that) {
Boolean expOk = Boolean.TRUE;
if (that.getTestExpression() instanceof Expression) {
Expression exp = (Expression) that.getTestExpression();
// Assignment cannot be used in this expression
expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of an if-then-else statement"));
}
//Update list of what has been assigned with one from test expression, because any variables it sees or assigns will be visible
//in the rest of the body.
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData testExpression_result = that.getTestExpression().visit(etc);
thingsThatHaveBeenAssigned = etc.thingsThatHaveBeenAssigned;
//Use a new visitor for the body of the then statement--it is a different lexical scope, so we want to track the
//variables seperately.
BodyTypeChecker btcThen = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData thenStatement_result = that.getThenStatement().visit(btcThen);
//Now, change any VariableDatas that were given a value in the ThenStatement back to having been unassigned
unassignVariableDatas(btcThen.thingsThatHaveBeenAssigned);
//Use a new visitor for the body of the else statement--it is a different lexical scope, so we want to track the
//variables seperately.
BodyTypeChecker btcElse = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData elseStatement_result = that.getElseStatement().visit(btcElse);
//Now, change any VariableDatas that were given a value in the ElseStatement back to having been unassigned
unassignVariableDatas(btcElse.thingsThatHaveBeenAssigned);
//Now compare the two lists of VariableDatas, and reassign those that were assigned in both branches.
reassignVariableDatas(btcThen.thingsThatHaveBeenAssigned, btcElse.thingsThatHaveBeenAssigned);
if (expOk.booleanValue()) {return forIfThenElseStatementOnly(that, testExpression_result, thenStatement_result, elseStatement_result);}
return null;
}
/* Make sure that the conditional expression has the right type, and if both branches of the
* if/else return, return a value the common super type of the two return types.
* We assume that thenStatement_result and elseStatement_result are InstanceDatas
*/
public TypeData forIfThenElseStatementOnly(IfThenElseStatement that, TypeData testExpression_result, TypeData thenStatement_result, TypeData elseStatement_result) {
if (testExpression_result != null && assertFound(testExpression_result, that.getTestExpression())) {
if (!testExpression_result.isInstanceType()) {
_addError("This if-then-else statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that);
}
else if (!testExpression_result.getSymbolData().isBooleanType(_targetVersion)) {
_addError("This if-then-else statement's conditional expression must be a boolean value. Instead, its type is " + testExpression_result.getName(), that.getTestExpression());
}
}
if (testExpression_result == null ||
thenStatement_result == null ||
elseStatement_result == null) { return null; }
// We don't throw an error here because if the then and else branches return incompatible types,
// there must have already been an error thrown in forValueReturnStatementOnly
// that indicates that one of the return statements is returning the wrong type.
SymbolData result = getCommonSuperType(thenStatement_result.getSymbolData(), elseStatement_result.getSymbolData());
if (result==null) {return null;}
return result.getInstanceData();
}
/*
* First, visit the condition expression of the while statement with a special visitor that
* makes sure no assignment is done.
* Then, visit the condition expression with the ExpressionTypeChecker which will do all the
* normal expression stuff.
* Then, visit the body with this visitor.
* Be very careful about maintaing the various scopes here.
*/
public TypeData forWhileStatement(WhileStatement that) {
Boolean expOk = Boolean.TRUE;
if (that.getCondition() instanceof Expression) {
Expression exp = (Expression) that.getCondition();
// Assignment cannot be used in this expression
expOk = exp.visit(new NoAssignmentAllowedInExpression("the condition expression of a while statement"));
}
//Visit the condition expression with an expression type checker and
//then update list of what was assigned, because it will always be visited so it is in the same scope as the enclosing body.
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData condition_result = that.getCondition().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
//Use a new visitor for the body of the while statement--it is a different lexical scope, so we want to track the
//variables seperately.
BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData code_result = that.getCode().visit(btc);
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
if (expOk.booleanValue()) {return forWhileStatementOnly(that, condition_result, code_result);}
return null;
}
/**Make sure that the condition statement of the while returns type boolean. */
public TypeData forWhileStatementOnly(WhileStatement that, TypeData condition_result, TypeData code_result) {
if (condition_result != null && assertFound(condition_result, that.getCondition())) {
if (!condition_result.isInstanceType()) {
_addError("This while-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that);
}
else if (!condition_result.getSymbolData().isBooleanType(_targetVersion)) {
_addError("This while-statement's conditional expression must be a boolean value. Instead, its type is " + condition_result.getName(), that.getCondition());
}
}
return null;
}
/*
* First, visit the body of the do statement with a body type checker.
* Then, visit the condition expression of the do statement with a special visitor that
* makes sure no assignment is done.
* Then, visit the condition expression with the ExpressionTypeChecker which will do all the
* normal expression stuff.
* Be very careful about maintaing the various scopes here.
*/
public TypeData forDoStatement(DoStatement that) {
//Use a new visitor for the body of the do statement--it is a different lexical scope, so we want to track the
//variables seperately.
BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
final TypeData code_result = that.getCode().visit(btc);
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
Boolean expOk = Boolean.TRUE;
if (that.getCondition() instanceof Expression) {
Expression exp = (Expression) that.getCondition();
// Assignment cannot be used in this expression
expOk = exp.visit(new NoAssignmentAllowedInExpression("the condition expression of a do statement"));
}
//Visit the condition statement with a new ExpressionTypeChecker, then update the thingsThatHaveBeenAssigned list, since it is in the same scope as the body.
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData condition_result = that.getCondition().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
if (expOk.booleanValue()) {return forDoStatementOnly(that, code_result, condition_result);}
return null;
}
/**Make sure that the condition statement of the while returns type boolean. */
public TypeData forDoStatementOnly(DoStatement that, TypeData code_result, TypeData condition_result) {
if (condition_result != null && assertFound(condition_result, that.getCondition())) {
if (!condition_result.isInstanceType()) {
_addError("This do-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that.getCondition());
}
else if (!condition_result.getSymbolData().isBooleanType(_targetVersion)) {
_addError("This do-statement's conditional expression must be a boolean value. Instead, its type is " + condition_result.getName(), that.getCondition());
}
}
if (code_result == null) {return null;}
return code_result.getInstanceData();
}
/*Handle the switch statement(explained within the method)*/
public TypeData forSwitchStatement(SwitchStatement that) {
Expression exp = that.getTest();
//make sure no assignments are made in the switch stmt expression
exp.visit(new NoAssignmentAllowedInExpression("the switch expression of a switch statement"));
//Visit the test with this visitor, because it is in the scope of the method
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData test_result = that.getTest().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
//The test result of a switch statement must be either an int or a char, according to the JLS.
if (test_result == null || !assertFound(test_result, exp)) {return null;}
if (!(_isAssignableFrom(SymbolData.INT_TYPE, test_result.getSymbolData()) || _isAssignableFrom(SymbolData.CHAR_TYPE, test_result.getSymbolData()))) {
_addError("The switch expression must be either an int or a char. You have used a " + test_result.getSymbolData().getName(), that.getTest());
}
final TypeData[] cases_result = makeArrayOfRetType(that.getCases().length);
BodyTypeChecker[] btcs = new BodyTypeChecker[that.getCases().length];
HashSet<Integer> labels = new HashSet<Integer>();
LinkedList<VariableData> variablesAssigned = new LinkedList<VariableData>();
boolean seenDefault = false;
boolean hadCaseReturn = false;
/**
* Loop over all the cases, type-checking them and then checking that no labels are duplicated
* and only one default statement is present.
*/
for (int i = 0; i < that.getCases().length; i++) {
SwitchCase sc = that.getCases()[i];
btcs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
cases_result[i] = sc.visit(btcs[i]);
if (sc instanceof LabeledCase) {
LabeledCase lc = (LabeledCase) sc;
//Get the label, make sure it is an int or char, and then put it in our hash set.
Integer toCheck = null;
if (lc.getLabel() instanceof CharLiteral) {
toCheck = (int) ((CharLiteral) lc.getLabel()).getValue();
}
if (lc.getLabel() instanceof IntegerLiteral) {
toCheck = ((IntegerLiteral) lc.getLabel()).getValue();
}
if (toCheck != null) {
if (labels.contains(toCheck)) {
_addError("You cannot have two switch cases with the same label " + toCheck, lc.getLabel());
}
else {
labels.add(toCheck);
}
}
}
else {
if (seenDefault) {
_addError("A switch statement can only have one default case", sc);
}
seenDefault = true;
}
//compare the variables assigned, and let variablesAssigned store the variables that have been assigned in every case
// that returns or breaks up to this one and are also assigned in this one
// if we're in the last case, we treat it as a break even though that may not happen explicitly
if (cases_result[i] != null || (i == cases_result.length - 1)) {
if (!hadCaseReturn) {
variablesAssigned = btcs[i].thingsThatHaveBeenAssigned;
hadCaseReturn = true;
}
else {
Iterator<VariableData> iter = variablesAssigned.iterator();
while (iter.hasNext()) {
if (!btcs[i].thingsThatHaveBeenAssigned.contains(iter.next())) {iter.remove();}
}
}
}
//Now, change any VariableDatas that were given a value in the case statement back to having been unassigned
unassignVariableDatas(btcs[i].thingsThatHaveBeenAssigned);
}
//Now assign a value to all variables that were assigned in each case branch, if there was a default case
if (seenDefault) {
for (VariableData vd : variablesAssigned) {
vd.gotValue();
}
}
return forSwitchStatementOnly(that, test_result, cases_result, seenDefault);
}
/**
* Here, we follow the following rules for determining what to return:
* If there is not a default block, the statement does not return.
* If the result from any of the blocks is KEEP_GOING, the statement does not return. (KEEP_GOING signifies that a break statement was seen)
* If the last block does not return, then the statement does not return.
*/
public TypeData forSwitchStatementOnly(SwitchStatement that, TypeData test_result, TypeData[] cases_result, boolean sawDefault) {
/**If we did not see a default block, this statement cannot be guaranteed to return*/
if (!sawDefault) {return null;}
/**If any of the blocks are KEEP_GOING, then the statement does not return.*/
for (int i = 0; i<cases_result.length; i++) {
if (cases_result[i] != null && cases_result[i].getSymbolData() == SymbolData.KEEP_GOING) {return null;}
}
/**If the last block does not return, then the statement also does not return. */
if (cases_result[cases_result.length-1] == null) { return null; }
return _bodyData.getMethodData().getReturnType().getInstanceData();
}
/**
* Make sure that the label for this LabeledCase is correct. The label must be a constant expression of type int or char.
* Then delegate to the super class to handle the braced body of the switch case.
*/
public TypeData forLabeledCase(LabeledCase that) {
ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
final TypeData label_result = that.getLabel().visit(etc);
thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
Expression exp = that.getLabel();
if (label_result == null || !assertFound(label_result, exp)) {
return null;
}
//we allow constant expressions of the form -5 or 5, but nothing else.
if (!(exp instanceof LexicalLiteral || exp instanceof NumericUnaryExpression && ((NumericUnaryExpression) exp).getValue() instanceof LexicalLiteral)) {
_addError("The labels of a switch statement must be constants. You are using a more complicated expression of type " + label_result.getSymbolData().getName(), that.getLabel());
}
else if (!_isAssignableFrom(SymbolData.INT_TYPE, label_result.getSymbolData())) {
_addError("The labels of a switch statement must be constants of int or char type. You specified a constant of type " + label_result.getSymbolData().getName(), that.getLabel());
}
return forSwitchCase(that);
}
/**
* Delegate handling this default case to its superclass.
*/
public TypeData forDefaultCase(DefaultCase that) {
return forSwitchCase(that);
}
/**
* Visit the Braced Body of this SwitchCase, and return the result.
*/
public TypeData forSwitchCase(SwitchCase that) {
final TypeData code_result = that.getCode().visit(this);
//If the case falls through (i.e. returns null) but has some statements in it, then there is an error.
//We only want to allow fall through for multiple labels, of the form:
/** 'a':
* 'b':
* return 5;
*/
if (code_result == null && that.getCode().getStatements().length > 0) {
_addError("You must end a non-empty switch case with a break or return statement at the Advanced level", that);
}
return code_result;
}
/*Visit the block, and update with what it assigns. Return the result of visiting the block.*/
public TypeData forBlock(Block that) {
BlockData bd = _bodyData.getNextBlock();
if (bd == null) { throw new RuntimeException("Internal Program Error: Enclosing body does not contain this block. Please report this bug"); }
BodyTypeChecker btc = createANewInstanceOfMe(bd, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
TypeData statements_result = that.getStatements().visit(btc);
thingsThatHaveBeenAssigned.addAll(btc.thingsThatHaveBeenAssigned);
return statements_result;
}
/*Try to resolve the type and make sure it can be referenced from this context (i.e. is accessible).*/
public TypeData forTypeOnly(Type that) {
Data sd = getSymbolData(that.getName(), _data, that);
while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
if (!checkAccessibility(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _bodyData.getSymbolData(), "class")) {
return null;
}
sd = sd.getOuterData();
}
return null;
}
/**
* Makes sure that no super class of any exception is caught before the current exception's catch block.
*/
protected void checkDuplicateExceptions(TryCatchStatement that) {
// Make sure that the user isn't throwing duplicate exceptions
LinkedList<SymbolData> catchBlockExceptions = new LinkedList<SymbolData>();
CatchBlock[] catchBlocks = that.getCatchBlocks();
for (int i = 0; i < catchBlocks.length; i++) {
catchBlockExceptions.addLast(getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(), _data, catchBlocks[i].getException()));
}
for (int i = 0; i < catchBlockExceptions.size(); i++) {
for (int j = i+1; j < catchBlockExceptions.size(); j++) {
if (catchBlockExceptions.get(j) != null && catchBlockExceptions.get(j).isSubClassOf(catchBlockExceptions.get(i))) {
_addError("Exception " + catchBlockExceptions.get(j).getName() + " has already been caught", catchBlocks[j].getException());
}
}
}
}
/**
* Check if the two given SymbolDatas have a common super type. If so, return it, else return null.
*/
protected SymbolData getCommonSuperType(SymbolData s1, SymbolData s2) {
if ((s1 == null) && (s2 == null)) {
return null;
}
if (s1 == SymbolData.KEEP_GOING && s2 != null) {return SymbolData.KEEP_GOING;}
if (s2 == SymbolData.KEEP_GOING && s1 != null) {return SymbolData.KEEP_GOING;}
if (s1 == null && s1 != SymbolData.KEEP_GOING) { return s2; }
if (s2 == null && s1 != SymbolData.KEEP_GOING) {return s1;}
if (s1==null || s2==null) {return null;}
if (s1 == SymbolData.EXCEPTION) { return s2; }
if (s2 == SymbolData.EXCEPTION) { return s1; }
// See if s1 and s2 have a common super class.
SymbolData sd = getCommonSuperTypeBaseCase(s1, s2);
if (sd != null ) { return sd; }
sd = getCommonSuperTypeBaseCase(s2, s1);
if (sd != null) { return sd; }
//If s1's superClass is null, then we have gone all the way through the superclass hierarchy without finding a matching class.
if (s1.getSuperClass() == null) {
//return null;
//since we know that Object should be the super class of everything, return Object.
return getSymbolData("java.lang.Object", _data, new NullLiteral(JExprParser.NO_SOURCE_INFO));
}
// Recur on the super class chain.
sd = getCommonSuperType(s1.getSuperClass(), s2);
if (sd != null) {
return sd;
}
// Recur on each interface.
for (SymbolData currSd : s1.getInterfaces()) {
sd = getCommonSuperType(currSd, s2);
if (sd != null) {
return sd;
}
}
return null;
}
/*@return true if the symbol data is the generic SymbolData.EXCEPTIOn class or if it extends java.lang.Throwable*/
protected boolean isException(SymbolData sd) {
return sd==SymbolData.EXCEPTION || sd.isSubClassOf(getSymbolData("java.lang.Throwable", new NullLiteral(JExprParser.NO_SOURCE_INFO), false, false));
}
/**
* Returns the least restrictive type returned by the try block and catch blocks or the finally block. Returns null if this
* try-catch statement doesn't necessarily return a value.
*/
protected InstanceData tryCatchLeastRestrictiveType(InstanceData tryBlock_result, InstanceData[] catchBlocks_result, InstanceData finallyBlock_result) {// Return the common superclass or null if there exists a block that doesn't return no nothing (except the finally block)
if (tryBlock_result == null || tryBlock_result == SymbolData.KEEP_GOING.getInstanceData()) {return finallyBlock_result;}
TypeData leastRestrictiveType = tryBlock_result;
for (int i = 0; i < catchBlocks_result.length; i++) {
if (catchBlocks_result[i] == null) {return finallyBlock_result;}
if (catchBlocks_result[i] != SymbolData.KEEP_GOING.getInstanceData() && _isAssignableFrom(catchBlocks_result[i].getSymbolData(), leastRestrictiveType.getSymbolData())) {
leastRestrictiveType = catchBlocks_result[i];
}
}
SymbolData result;
if (leastRestrictiveType == null && finallyBlock_result == null) {return null;}
else if (leastRestrictiveType == null) {result = getCommonSuperType(null, finallyBlock_result.getSymbolData());}
else if (finallyBlock_result == null) {result = getCommonSuperType(leastRestrictiveType.getSymbolData(), null);}
else { result = getCommonSuperType(leastRestrictiveType.getSymbolData(), finallyBlock_result.getSymbolData());}
if (result != null) {return result.getInstanceData();}
return null;
}
/**
* Return true if the Exception is unchecked, and false otherwise.
* An exception is unchecked if it does not extend either java.lang.RuntimeException or java.lang.Error,
* and is not declared to be thrown by the enclosing method.
* @param sd The SymbolData of the Exception we are checking.
* @param that The JExpression passed to getSymbolData for error purposes.
*/
public boolean isUncaughtCheckedException(SymbolData sd, JExpression that) {
if (isCheckedException(sd, that)) {
MethodData md = _bodyData.getMethodData();
for (int i = 0; i<md.getThrown().length; i++) {
//If the Exception matches an exception declared to be thrown by the enclosing method, it is not unchecked.
if (sd.isSubClassOf(getSymbolData(md.getThrown()[i], _data, that))) {return false;}
}
return true;
}
return false;
}
/**
* Make sure that every exception that is caught could have been thrown in the try statement
*/
protected void makeSureCaughtStuffWasThrown(TryCatchStatement that, SymbolData[] caught_array, LinkedList<Pair<SymbolData, JExpression>> thrown) {
// Make sure every Exception that is caught could actually be thrown
for (int i = 0; i < caught_array.length; i++) {
SymbolData currCaughtSD = caught_array[i];
boolean foundThrownException = false;
if (isCheckedException(currCaughtSD, that) &&
! currCaughtSD.getName().equals("java.lang.Exception") &&
! currCaughtSD.getName().equals("java.lang.Throwable")) {
for (Pair<SymbolData, JExpression> p : thrown) {
SymbolData sd = p.getFirst();
if (sd.isSubClassOf(currCaughtSD)) {
foundThrownException = true;
}
}
if (!foundThrownException) {
_addError("The exception " + currCaughtSD.getName() + " is never thrown in the body of the corresponding try block", that.getCatchBlocks()[i]);
}
}
}
}
/**
* Make sure that every Exception in thrown is either in caught or in the list of what can be thrown from where we are.
* Also make sure that every Exception that is declared to be thrown or caught is actually thrown.
* @param that The TryCatchStatement we are currently working with
* @param caught_array The SymbolData[] of exceptions that are explicitely caught.
* @param thrown The LinkedList of SymbolData of exceptions that are thrown. This will be modified.
*/
protected void compareThrownAndCaught(TryCatchStatement that, SymbolData[] caught_array, LinkedList<Pair<SymbolData, JExpression>> thrown) {
LinkedList<SymbolData> caught = new LinkedList<SymbolData>();
for (int i = 0; i<caught_array.length; i++) {
caught.addLast(caught_array[i]);
}
//Make sure that every Exception in thrown is either caught or in the list of what can be thrown
for (Pair<SymbolData, JExpression> p : thrown) {
SymbolData sd = p.getFirst();
JExpression j = p.getSecond();
if (isUncaughtCheckedException(sd, j)) {
boolean foundCatchBlock = false;
for (SymbolData currCaughtSD : caught) {
if (sd.isSubClassOf(currCaughtSD)) {
foundCatchBlock = true;
}
}
// Check if this exception is a checked exception and is not declared to be thrown by the enclosing method
//This is a checked exception. It should have been caught.
if (!foundCatchBlock) {
handleUncheckedException(sd, j);
}
}
}
makeSureCaughtStuffWasThrown(that, caught_array, thrown);
}
/**
* Assumes that tryBlock_result, catchBlocks_result, and finallyBlock_result are InstanceDatas
*/
public TypeData forTryCatchFinallyStatementOnly(TryCatchFinallyStatement that, TypeData tryBlock_result, TypeData[] catchBlocks_result, TypeData finallyBlock_result) {
checkDuplicateExceptions(that);
//we know ids are instance datas, but we have to do this to cast them properly.
InstanceData[] ids = new InstanceData[catchBlocks_result.length];
for (int i = 0; i<ids.length; i++) {
if (catchBlocks_result[i] != null) {
ids[i]=catchBlocks_result[i].getInstanceData();
}
else {ids[i]=null;}
}
/**Make sure null pointer exceptions don't happen.*/
if (tryBlock_result == null && finallyBlock_result==null) {return tryCatchLeastRestrictiveType(null, ids, null);}
if (tryBlock_result == null) {return tryCatchLeastRestrictiveType(null, ids, finallyBlock_result.getInstanceData());}
if (finallyBlock_result == null) {return tryCatchLeastRestrictiveType(tryBlock_result.getInstanceData(), ids, null);}
return tryCatchLeastRestrictiveType(tryBlock_result.getInstanceData(), ids, finallyBlock_result.getInstanceData());
}
/*Visit the try block, catch blocks, and finally block. Add any exceptions that are not caught to thrown.*/
public TypeData forTryCatchFinallyStatement(TryCatchFinallyStatement that) {
BodyTypeChecker btc = new TryCatchBodyTypeChecker(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), new LinkedList<Pair<SymbolData, JExpression>>());
final TypeData tryBlock_result = that.getTryBlock().visit(btc);
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
BodyTypeChecker[] catchTCs = new BodyTypeChecker[that.getCatchBlocks().length];
LinkedList<LinkedList<VariableData>> catchVars = new LinkedList<LinkedList<VariableData>>();
CatchBlock[] catchBlocks = that.getCatchBlocks();
final TypeData[] catchBlocks_result = makeArrayOfRetType(catchBlocks.length);
final SymbolData[] caughtExceptions = new SymbolData[catchBlocks.length];
for (int i = 0; i < catchBlocks.length; i++) {
catchTCs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
catchBlocks_result[i] = catchBlocks[i].visit(catchTCs[i]);
unassignVariableDatas(catchTCs[i].thingsThatHaveBeenAssigned);
catchVars.addLast(catchTCs[i].thingsThatHaveBeenAssigned);
caughtExceptions[i] = getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(), _data, catchBlocks[i]);
}
BodyTypeChecker btcFinally = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
TypeData finallyBlock_result = that.getFinallyBlock().visit(btcFinally);
//leave anything that was assigned in the finally block assigned. It is always visited.
//Give values to anything assigned in the try and catch blocks.
reassignLotsaVariableDatas(btc.thingsThatHaveBeenAssigned, catchVars);
//If the finallyBlock didn't end abruptly, need to check thrown and caught
if (finallyBlock_result == null) {
compareThrownAndCaught(that, caughtExceptions, btc._thrown);
}
else { /**It is like thrown is empty--any exceptions in it don't need to be caught.*/
_thrown = new LinkedList<Pair<SymbolData, JExpression>>();
}
if (finallyBlock_result == SymbolData.KEEP_GOING.getInstanceData()) {
finallyBlock_result = null;
}
// Add the exceptions thrown by btc to our thrown list.
if (this instanceof TryCatchBodyTypeChecker) {
_thrown.addAll(btc._thrown);
}
return forTryCatchFinallyStatementOnly(that, tryBlock_result, catchBlocks_result, finallyBlock_result);
}
/*
* Resolve the type of the exception, and visit the body, making sure the exception variable
* is in scope.
*/
public TypeData forCatchBlock(CatchBlock that) {
VariableDeclarator dec = that.getException().getDeclarator();
SymbolData exception_result = getSymbolData(dec.getType().getName(), _data, dec.getType());
BlockData bd = _bodyData.getNextBlock();
if (bd == null) { throw new RuntimeException("Internal Program Error: Enclosing body does not contain this block. Please report this bug"); }
VariableData vd = bd.getVar(dec.getName().getText());
if (vd == null) { throw new RuntimeException("Internal Program Error: Catch block does not contain its exception variable. Please report this bug"); }
LinkedList<VariableData> newVars = cloneVariableDataList(_vars);
newVars.addLast(vd);
BodyTypeChecker btc = createANewInstanceOfMe(bd, _file, _package, _importedFiles, _importedPackages, newVars, _thrown);
TypeData block_result = that.getBlock().getStatements().visit(btc);
thingsThatHaveBeenAssigned.addAll(btc.thingsThatHaveBeenAssigned);
return forCatchBlockOnly(that, exception_result, block_result);
}
/*Return the result of visiting the body*/
public TypeData forCatchBlockOnly(CatchBlock that, SymbolData exception_result, TypeData block_result) {
return block_result;
}
/**Assumes that tryBlock_result, catchBlocks_result, and finallyBlock_result are InstanceDatas*/
public TypeData forNormalTryCatchStatementOnly(NormalTryCatchStatement that, TypeData tryBlock_result, TypeData[] catchBlocks_result) {
checkDuplicateExceptions(that);
InstanceData[] ids = new InstanceData[catchBlocks_result.length];
for (int i = 0; i<catchBlocks_result.length; i++) {
ids[i]=(InstanceData) catchBlocks_result[i];
}
return tryCatchLeastRestrictiveType((InstanceData) tryBlock_result, ids, null);
}
/*no finally block*/
public TypeData forNormalTryCatchStatement(NormalTryCatchStatement that) {
BodyTypeChecker btc = new TryCatchBodyTypeChecker(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), new LinkedList<Pair<SymbolData, JExpression>>());
final TypeData tryBlock_result = that.getTryBlock().visit(btc);
unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
LinkedList<LinkedList<VariableData>> catchVars = new LinkedList<LinkedList<VariableData>>();
BodyTypeChecker[] catchTCs = new BodyTypeChecker[that.getCatchBlocks().length];
CatchBlock[] catchBlocks = that.getCatchBlocks();
final TypeData[] catchBlocks_result = makeArrayOfRetType(catchBlocks.length);
final SymbolData[] caughtExceptions = new SymbolData[catchBlocks.length];
for (int i = 0; i < catchBlocks.length; i++) {
catchTCs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
catchBlocks_result[i] = catchBlocks[i].visit(catchTCs[i]);
unassignVariableDatas(catchTCs[i].thingsThatHaveBeenAssigned);
catchVars.addLast(catchTCs[i].thingsThatHaveBeenAssigned);
caughtExceptions[i] = getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(), _data, catchBlocks[i]);
}
//Give values to anything assigned in the try and catch blocks.
reassignLotsaVariableDatas(btc.thingsThatHaveBeenAssigned, catchVars);
compareThrownAndCaught(that, caughtExceptions, btc._thrown);
// Add the exceptions thrown by btc to our thrown list.
if (this instanceof TryCatchBodyTypeChecker) {
_thrown.addAll(btc._thrown);
}
return forNormalTryCatchStatementOnly(that, tryBlock_result, catchBlocks_result);
}
/*A special visitor that does not allow assignment in any expressions*/
private class NoAssignmentAllowedInExpression extends JExpressionIFAbstractVisitor<Boolean> {
String _location;
private NoAssignmentAllowedInExpression(String location) {
_location = location;
}
/**
* Most expressions do not involve assignment
*/
public Boolean defaultCase(JExpressionIF that) {
return Boolean.TRUE;
}
/*Throw an appropriate error*/
public Boolean forIncrementExpression(IncrementExpression that) {
_addError("You cannot use an increment or decrement expression in " + _location + " at any language level", that);
return Boolean.FALSE;
}
/*Throw an appropriate error*/
public Boolean forAssignmentExpression(AssignmentExpression that) {
_addError("You cannot use an assignment expression in " + _location + " at any language level", that);
return Boolean.FALSE;
}
/*Throw an appropriate error*/
public Boolean forSimpleAssignmentExpression(SimpleAssignmentExpression that) {
_addError("You cannot use an assignment expression in " + _location + " at any language level" + ". Perhaps you meant to compare two values with '=='", that);
return Boolean.FALSE;
}
}
/**
* Test the methods in the above class.
*/
public static class BodyTypeCheckerTest extends TestCase {
private BodyTypeChecker _bbtc;
private BodyData _bd1;
private BodyData _bd2;
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 BodyTypeCheckerTest() {
this("");
}
public BodyTypeCheckerTest(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");
_bd1 = new MethodData("methodName1",
_packageMav,
new TypeParameter[0],
SymbolData.INT_TYPE,
new VariableData[] { new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null), new VariableData(SymbolData.BOOLEAN_TYPE) },
new String[0],
_sd1,
null); // no SourceInfo
((MethodData)_bd1).getParams()[0].setEnclosingData(_bd1);
((MethodData)_bd1).getParams()[1].setEnclosingData(_bd1);
_bd2 = new MethodData("methodName2",
_packageMav,
new TypeParameter[0],
SymbolData.VOID_TYPE,
new VariableData[] { new VariableData(SymbolData.INT_TYPE) },
new String[0],
_sd1,
null); // no SourceInfo);
((MethodData)_bd2).getParams()[0].setEnclosingData(_bd2);
errors = new LinkedList<Pair<String, JExpressionIF>>();
symbolTable = new Symboltable();
_bd1.addEnclosingData(_sd1);
_bd1.addVars(((MethodData)_bd1).getParams());
_bd2.addVars(((MethodData)_bd2).getParams());
_bbtc = new BodyTypeChecker(_bd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData,JExpression>>());
_bbtc._targetVersion = JavaVersion.JAVA_5;
_bbtc._importedPackages.addFirst("java.lang");
}
public void testForUninitializedVariableDeclaratorOnly() {
VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _bd1);
_bd1.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(_bbtc);
assertTrue("_vars should contain Mojo.", _bbtc._vars.contains(vd1));
}
public void testForInitializedVariableDeclaratorOnly() {
_bbtc.symbolTable.put("int", SymbolData.INT_TYPE);
VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, true, _bd1);
_bd1.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(_bbtc);
assertEquals("There should be no errors.", 0, errors.size());
assertTrue("_vars should contain Mojo.", _bbtc._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(_bbtc);
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 testForBracedBodyOnly() {
// Test one that works.
BracedBody bb1 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1))});
TypeData sd = bb1.visit(_bbtc);
assertEquals("There should be no errors", 0, errors.size());
assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
BracedBody bb2 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new CharLiteral(JExprParser.NO_SOURCE_INFO, 'e'))});
//test another one that works.
sd = bb2.visit(_bbtc);
assertEquals("There should be no errors", 0, errors.size());
assertEquals("Should return char type", SymbolData.CHAR_TYPE.getInstanceData(), sd);
BracedBody bb3 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1)),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new CharLiteral(JExprParser.NO_SOURCE_INFO, 'e'))});
//test one that should throw an error: unreachable return statement.
sd = bb3.visit(_bbtc);
assertEquals("There should be one error", 1, errors.size());
assertEquals("The error message should be correct", "Unreachable statement.", errors.get(0).getFirst());
assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
BracedBody bb4 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[0]);
//test empty body. should return null.
sd = bb4.visit(_bbtc);
assertEquals("There should still be one error", 1, errors.size());
assertEquals("The error message should still be be correct", "Unreachable statement.", errors.get(0).getFirst());
assertEquals("Should return null", null, sd);
}
public void testForVoidReturnStatementOnly() {
_bbtc._bodyData = _bd2; //this body data has a void return type
//test one that works
BracedBody bb1 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new VoidReturnStatement(JExprParser.NO_SOURCE_INFO)});
TypeData sd = bb1.visit(_bbtc);
assertEquals("There should be no errors.", 0, errors.size());
assertEquals("Should return void type.", SymbolData.VOID_TYPE.getInstanceData(), sd);
//test with a method that doesn't return void.
_bbtc._bodyData = _bd1;
sd = bb1.visit(_bbtc);
assertEquals("There should be one error", 1, errors.size());
assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
assertEquals("Error message should be correct", "Cannot return void when the method's expected return type is not void.", errors.get(0).getFirst());
}
public void testforValueReturnStatementOnly() {
//value return statement returns something that is not a subclass the method return type
BracedBody bb1 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true))});
TypeData sd = bb1.visit(_bbtc);
assertEquals("There should be one error", 1, errors.size());
assertEquals("Should return boolean type", SymbolData.BOOLEAN_TYPE.getInstanceData(), sd);
assertEquals("Error message should be correct", "This method expected to return type: \"int\" but here returned type: \"boolean\".", errors.get(0).getFirst());
//value return statement returns something that is assignable from the method return type
BracedBody bb2 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new CharLiteral(JExprParser.NO_SOURCE_INFO, 'c'))});
sd = bb2.visit(_bbtc);
assertEquals("There should be still be one error", 1, errors.size());
assertEquals("Should return char type", SymbolData.CHAR_TYPE.getInstanceData(), sd);
assertEquals("Error message should still be correct", "This method expected to return type: \"int\" but here returned type: \"boolean\".", errors.get(0).getFirst());
//method returns void
_bbtc._bodyData = _bd2; //this body data has a void return type
BracedBody bb3 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1))});
sd = bb3.visit(_bbtc);
assertEquals("There should be two errors", 2, errors.size());
assertEquals("Should return void type", SymbolData.VOID_TYPE.getInstanceData(), sd);
assertEquals("Error message should be correct", "Cannot return a value when the method's expected return type is void.", errors.get(1).getFirst());
// Test where the return value is a class name.
BracedBody bb4 = new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "int")))});
sd = bb4.visit(_bbtc);
assertEquals("There should be 3 errors", 3, errors.size());
assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
assertEquals("Error message should be correct", "You cannot return a class or interface name. Perhaps you meant to say int.class or to create an instance", errors.getLast().getFirst());
}
public void testForIfThenElseStatementOnly() {
//test if the expression is not of boolean type
IfThenElseStatement ites1 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 1),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 4)),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new CharLiteral(JExprParser.NO_SOURCE_INFO, 'j')));
TypeData sd = ites1.visit(_bbtc);
assertEquals("There should be one error", 1, errors.size());
assertEquals("Error message should be correct", "This if-then-else statement's conditional expression must be a boolean value. Instead, its type is int", errors.get(0).getFirst());
assertEquals("Should return integer type", SymbolData.INT_TYPE.getInstanceData(), sd);
//test if the branches do not return subtypes of each other
IfThenElseStatement ites2 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 4)),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true)));
sd = ites2.visit(_bbtc);
assertEquals("There should be two errors", 2, errors.size());
assertEquals("Should return Object type", "java.lang.Object", sd.getName());
assertEquals("Error message should be correct",
"This method expected to return type: \"int\" but here returned type: \"boolean\".",
errors.get(1).getFirst());
//test if they do return subtypes of each other
IfThenElseStatement ites3 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 4)),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new CharLiteral(JExprParser.NO_SOURCE_INFO, 'f')));
sd = ites3.visit(_bbtc);
assertEquals("There should still be two errors", 2, errors.size());
assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
//test if neither branch returns
IfThenElseStatement ites4 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true),
new EmptyStatement(JExprParser.NO_SOURCE_INFO),
new EmptyStatement(JExprParser.NO_SOURCE_INFO));
sd = ites4.visit(_bbtc);
assertEquals("There should still be two errors", 2, errors.size());
assertEquals("Should return null type", null, sd);
//test if only one branch returns
IfThenElseStatement ites5 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new BooleanLiteral(JExprParser.NO_SOURCE_INFO, true),
new EmptyStatement(JExprParser.NO_SOURCE_INFO),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 3)));
sd = ites5.visit(_bbtc);
assertEquals("There should still be two errors", 2, errors.size());
assertEquals("Should return null type", null, sd);
// Test for the word "boolean" as the condition.
IfThenElseStatement ites6 = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO,
new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "boolean")),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 4)),
new ValueReturnStatement(JExprParser.NO_SOURCE_INFO, new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 4)));
sd = ites6.visit(_bbtc);
assertEquals("There should be 3 errors", 3, errors.size());
assertEquals("Should return Integer type", SymbolData.INT_TYPE.getInstanceData(), sd);
assertEquals("Error message should be correct",
"This if-then-else statement's conditional expression must be a boolean value. Instead, it is a class or interface name",
errors.get(2).getFirst());
}
public void testForBlock() {
//Check that a block can reference fields in its enclosing method.
Block b = new Block(JExprParser.NO_SOURCE_INFO,
new BracedBody(JExprParser.NO_SOURCE_INFO,
new BodyItemI[] { new ValueReturnStatement(JExprParser.NO_SOURCE_INFO,
new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")))}));
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
LinkedList<VariableData> vars = new LinkedList<VariableData>();
vars.addLast(new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, _bd1));
_bbtc._vars = vars;
TypeData sd = b.visit(_bbtc);
assertEquals("There should not be any errors.", 0, errors.size());
assertEquals("Should return int type.", SymbolData.INT_TYPE.getInstanceData(), sd);
}
public void testForIfThenStatementOnly() {
SymbolData sd1 = SymbolData.BOOLEAN_TYPE;
SymbolData sd2 = SymbolData.INT_TYPE;
IfThenStatement its = new IfThenStatement(JExprParser.NO_SOURCE_INFO,
new NullLiteral(JExprParser.NO_SOURCE_INFO),
new EmptyStatement(JExprParser.NO_SOURCE_INFO));
//test a correct condition type
assertEquals("sd1 is boolean type, so should not add error. Returns null.", null,
_bbtc.forIfThenStatementOnly(its, sd1.getInstanceData(), null));
assertEquals("No errors should have been added", 0, errors.size());
//test an incorrect condition type
assertEquals("sd2 is not boolean type, so should add error. Returns null.", null,
_bbtc.forIfThenStatementOnly(its, sd2.getInstanceData(), null));
assertEquals("Should now be one error.", 1, errors.size());
assertEquals("Error message should be correct.", "This if-then-statement's conditional expression must be a boolean value. Instead, its type is int", errors.getLast().getFirst());
//test "bool" as a condition
assertEquals("sd1 is not an instance, so should add error. Returns null.", null,
_bbtc.forIfThenStatementOnly(its, sd1, null));
assertEquals("Should now be 2 errors.", 2, errors.size());
assertEquals("Error message should be correct.", "This if-then-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", errors.getLast().getFirst());
}
public void testForIfThenStatement() {
//Test that the proper variable assignment happens.
//here, a variable is only assigned in the then branch, so it should not be set after it returns.
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);
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"));
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});
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);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
LinkedList<VariableData> vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
//Here, a variable is assigned before the if, so it should still have a value after the if.
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertTrue("vd2 should also be assigned", vd2.hasValue());
//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});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
errors = new LinkedList<Pair<String, JExpressionIF>>();
// Test that an assignment in the if-expression throws an error
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));
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)));
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});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
te = new PlusAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")),
new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5));
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)));
ift = new IfThenStatement(JExprParser.NO_SOURCE_INFO, te, ts);
ift.visit(_bbtc);
assertEquals("There should now be one error", 1, errors.size());
assertEquals("Error message should be correct", "You cannot use an assignment expression in the conditional expression of an if-then statement at any language level", errors.get(0).getFirst());
}
public void testForIfThenElseStatement() {
//Test that the proper variable assignment happens.
//here, a variable is only assigned in the then branch, so it should not be set after it returns.
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)));
IfThenElseStatement ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
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"));
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});
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);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
LinkedList<VariableData> vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
//test that if a variable is only assigned in the else case that it is not assigned afterwards
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)));
ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement}));
ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, new EmptyStatement(JExprParser.NO_SOURCE_INFO), ts);
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
//Here, a variable is assigned before the if, so it should still have a value after the if.
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertTrue("vd2 should also be assigned", vd2.hasValue());
//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));
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 IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
errors = new LinkedList<Pair<String, JExpressionIF>>();
//test that if a variable is assigned in the then case that it cannot be used in the else case.
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));
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)));
ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement}));
ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new ExpressionStatement(JExprParser.NO_SOURCE_INFO, new EqualsExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word (JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 32))));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be one 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());
//test that if a variable is assigned in both cases that it is assigned afterwards
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));
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)));
ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement}));
ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, ts);
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertTrue("vd2 should be assigned", vd2.hasValue());
assertEquals("There should be one error", 1, errors.size());
//Test that if assignment is used in the conditional expression, an error is thrown
te = new PlusAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "i")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5));
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)));
ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement}));
ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, new EmptyStatement(JExprParser.NO_SOURCE_INFO), ts);
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ift.visit(_bbtc);
assertEquals("There should now be two errors", 2, errors.size());
assertEquals("The error message should be correct", "You cannot use an assignment expression in the conditional expression of an if-then-else statement at any language level", errors.get(1).getFirst());
//test that if one branch returns a value but the other is a break or continue that SymbolData.KEEP_GOING is returned.
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));
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[] {returnStatement}));
BreakStatement bs = new UnlabeledBreakStatement(JExprParser.NO_SOURCE_INFO);
ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, bs);
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
assertEquals("Should return SymbolData.KEEP_GOING", SymbolData.KEEP_GOING.getInstanceData(), ift.visit(_bbtc));
assertEquals("There should still be two errors", 2, errors.size());
}
public void testForForStatement() {
//Test that the proper variable assignment happens.
//here, a variable is only assigned in the for init, so it should not be set after it returns.
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));
UnparenthesizedExpressionList sel = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {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))});
ForStatement fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, te, new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[0]), new EmptyStatement(JExprParser.NO_SOURCE_INFO));
// IfThenElseStatement ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
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"));
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}), fs});
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);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
LinkedList<VariableData> vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
fs.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
//test that if a variable is testForForStdeclared in the for init that it has a value in the scope of the for statement, but not afterwards
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));
VariableDeclaration vd = new VariableDeclaration (JExprParser.NO_SOURCE_INFO, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), new Word(JExprParser.NO_SOURCE_INFO, "i"), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 10))});
UnparenthesizedExpressionList sel2 = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {te});
fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, te, sel2, new ExpressionStatement(JExprParser.NO_SOURCE_INFO, te));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {fs});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
fs.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
//here, a variable is only assigned in the for init and the for body, so it should not be set after it returns.
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)));
// te = new Expression(JExprParser.NO_SOURCE_INFO, new ExpressionPiece[] { new Word(JExprParser.NO_SOURCE_INFO, "j"),
// new Operator(JExprParser.NO_SOURCE_INFO, "<"), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5)});
sel = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {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))});
fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, new EmptyForCondition(JExprParser.NO_SOURCE_INFO), new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[0]), new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {ts})));
// IfThenElseStatement ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), fs});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
fs.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
assertEquals("There should be no errors", 0, errors.size());
//here, a variable is assigned before the for init, so it should still be set after it returns.
// te = new Expression(JExprParser.NO_SOURCE_INFO, new ExpressionPiece[] { new Word(JExprParser.NO_SOURCE_INFO, "j"),
// new Operator(JExprParser.NO_SOURCE_INFO, "<"), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5)});
sel = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {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))});
fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, new EmptyForCondition(JExprParser.NO_SOURCE_INFO), new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[0]), new EmptyStatement(JExprParser.NO_SOURCE_INFO));
// IfThenElseStatement ift = new IfThenElseStatement(JExprParser.NO_SOURCE_INFO, te, ts, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {new VariableDeclaration(JExprParser.NO_SOURCE_INFO, _packageMav, new UninitializedVariableDeclarator[]{uvd}), fs});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
fs.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertTrue("vd2 should be assigned", vd2.hasValue());
assertEquals("Should be 0 errors", 0, errors.size());
// make sure that assignment is not allowed in the conditional of the for statement
te = new PlusAssignmentExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 5));
vd = new VariableDeclaration (JExprParser.NO_SOURCE_INFO, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), new Word(JExprParser.NO_SOURCE_INFO, "i"), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 10))});
sel2 = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {te});
fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, te, sel2, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
bb = new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {fs});
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);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, cmd1);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
te = new PositivePrefixIncrementExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE_INFO, new Word(JExprParser.NO_SOURCE_INFO, "j")));
vd = new VariableDeclaration (JExprParser.NO_SOURCE_INFO, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(JExprParser.NO_SOURCE_INFO, new PrimitiveType(JExprParser.NO_SOURCE_INFO, "int"), new Word(JExprParser.NO_SOURCE_INFO, "i"), new IntegerLiteral(JExprParser.NO_SOURCE_INFO, 10))});
sel2 = new UnparenthesizedExpressionList(JExprParser.NO_SOURCE_INFO, new Expression[] {te});
fs = new ForStatement(JExprParser.NO_SOURCE_INFO, sel, te, sel2, new EmptyStatement(JExprParser.NO_SOURCE_INFO));
fs.visit(_bbtc);
assertEquals("There should be 1 error", 1, errors.size());
assertEquals("The error message should be correct", "You cannot use an increment or decrement expression in the conditional expression of a for-statement at any language level", errors.get(0).getFirst());
}
public void testForWhileStatement() {
//Test that a variable without a value before the while statement, that is given a value in the body of the while statement, still doesn't have a value afterwards
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 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 ts = new Block(JExprParser.NO_SOURCE_INFO, new BracedBody(JExprParser.NO_SOURCE_INFO, new BodyItemI[] {assignStatement}));
WhileStatement ws = new WhileStatement(JExprParser.NO_SOURCE_INFO, te, ts);
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, null);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
LinkedList<VariableData> vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ws.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertFalse("vd2 should not be assigned", vd2.hasValue());
//Test that a variable with a value before the while statement, that is given a value in the body of the while statement, still has a value afterwards
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, null);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ws.visit(_bbtc);
assertTrue("vd1 should be assigned", vd1.hasValue());
assertTrue("vd2 should be assigned", vd2.hasValue());
//Test that assignment is not allowed in the condition expression of the while
te = 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));
ws = new WhileStatement(JExprParser.NO_SOURCE_INFO, te, ts);
vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
new VariableData[] {vd1}, new String[0], _sd1, null);
vd1.setEnclosingData(md1);
vd2.setEnclosingData(md1);
md1.addVar(vd1);
md1.addVar(vd2);
vars = new LinkedList<VariableData>();
vars.addLast(vd1);
vars.addLast(vd2);
_bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
_bbtc._bodyData = md1;
_bbtc._data = md1;
_bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
ws.visit(_bbtc);
assertEquals("There should be 1 error", 1, errors.size());
assertEquals("The error message should be correct", "You cannot use an assignment expression in the condition expression of a while statement at any language level. Perhaps you meant to compare two values with '=='", errors.get(0).getFirst());
}
public void testForWhileStatementOnly() {
//Test that a boolean condition expression results in no error
Expression te = new LessThanExpression(JExprParser.NO_SOURCE_INFO, new SimpleNameReference(JExprParser.NO_SOURCE