001 /*BEGIN_COPYRIGHT_BLOCK
002 *
003 * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 * * Redistributions of source code must retain the above copyright
009 * notice, this list of conditions and the following disclaimer.
010 * * Redistributions in binary form must reproduce the above copyright
011 * notice, this list of conditions and the following disclaimer in the
012 * documentation and/or other materials provided with the distribution.
013 * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014 * names of its contributors may be used to endorse or promote products
015 * derived from this software without specific prior written permission.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028 *
029 * This software is Open Source Initiative approved Open Source Software.
030 * Open Source Initative Approved is a trademark of the Open Source Initiative.
031 *
032 * This file is part of DrJava. Download the current version of this project
033 * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034 *
035 * END_COPYRIGHT_BLOCK*/
036
037 package edu.rice.cs.javalanglevels;
038
039 import edu.rice.cs.javalanglevels.tree.*;
040 import edu.rice.cs.javalanglevels.parser.JExprParser;
041 import java.util.*;
042 import java.io.*;
043 import edu.rice.cs.plt.reflect.JavaVersion;
044 import edu.rice.cs.plt.iter.*;
045
046 import junit.framework.TestCase;
047
048 /** TypeChecks the context of a body, such as a method body. Common to all Language Levels.*/
049 public class BodyTypeChecker extends SpecialTypeChecker {
050 /** The MethodData of this method. */
051 protected BodyData _bodyData;
052
053 /** Constructor for BodyTypeChecker. Calls the super constructor for everything except bodyData, which we store here
054 * in order to have the proper type at compile time. (SpecialTypeChecker stores it as a Data).
055 * @param bodyData The enclosing BodyData for the context we are type checking.
056 * @param file The File corresponding to the source file.
057 * @param packageName The package name from the source file.
058 * @param importedFiles The names of the files that are specifically imported (through a class import statement) in
059 * the source file.
060 * @param importedPackages The names of all the packages that are imported through a package import statement in the
061 * source file.
062 * @param vars The list of VariableData that have already been defined (used so we can make sure we don't use a
063 * variable before it has been defined).
064 * @param thrown The list of exceptions thrown in this body
065 */
066 public BodyTypeChecker(BodyData bodyData, File file, String packageName, LinkedList<String> importedFiles,
067 LinkedList<String> importedPackages, LinkedList<VariableData> vars,
068 LinkedList<Pair<SymbolData, JExpression>> thrown) {
069 super(bodyData, file, packageName, importedFiles, importedPackages, vars, thrown);
070 _bodyData = bodyData;
071 }
072
073 /** @return the bodyData (enclosing data) for this context. */
074 protected Data _getData() { return _bodyData; }
075
076 /** @return the instance data of the class/interface enclosing this body data. */
077 public TypeData forSimpleThisReferenceOnly(SimpleThisReference that) {
078 return _bodyData.getSymbolData().getInstanceData();
079 }
080
081 /** @return the instance data of the super class of the class enclosing this body data. */
082 public TypeData forSimpleSuperReferenceOnly(SimpleSuperReference that) {
083 return _bodyData.getSymbolData().getSuperClass().getInstanceData();
084 }
085
086 /** Create a new instance of this class for visiting inner bodies. */
087 protected BodyTypeChecker createANewInstanceOfMe(BodyData bodyData,
088 File file,
089 String pakage,
090 LinkedList<String> importedFiles,
091 LinkedList<String> importedPackages,
092 LinkedList<VariableData> vars,
093 LinkedList<Pair<SymbolData, JExpression>> thrown) {
094 return new BodyTypeChecker(bodyData, file, pakage, importedFiles, importedPackages, vars, thrown);
095 }
096
097 /* There is currently no way to differentiate between a block statement and
098 * an instance initializer in a braced body given the general nature of a
099 * braced body. Whenever an instance initialization is visited in a method
100 * body, we must assume that it is a block statement.
101 */
102 public TypeData forInstanceInitializer(InstanceInitializer that) {
103 return forBlock(that.getCode());
104 }
105
106 /** We need to do this so that expressions (which should only occur in variable initializers and
107 * initializer blocks) can know which fields have already been declared. Add all the variable
108 * datas that are declared in this declarator to the list of variables that are visibile from where we are.
109 */
110 public TypeData forUninitializedVariableDeclaratorOnly(UninitializedVariableDeclarator that,
111 TypeData typeRes,
112 TypeData nameRes) {
113 _vars.addLast(_bodyData.getVar(that.getName().getText()));
114 return null;
115 }
116
117
118 /** Look at the result of each item in the body. If one is not null and does not correspond to an Expression
119 * Statement, then that means that that statement returns a value. Check to make sure that there are no
120 * statements following it. If there are, then those statements are unreachable so give an error.
121 * @param that The Body we were type checking
122 * @param items_result Array of results for each item in the body that was visited.
123 */
124 public TypeData forBodyOnly(Body that, TypeData[] items_result) {
125 for (int i = 0; i < items_result.length; i++) {
126 if (items_result[i] != null && !(that.getStatements()[i] instanceof ExpressionStatement)) {
127
128 // Found a statement that returns a value.
129 if (i < items_result.length - 1) {
130
131 // there must be unreachable statements
132 _addError("Unreachable statement.", (JExpression)that.getStatements()[i+1]);
133 }
134 // either way, return the result to keep on type-checking.
135 return items_result[i];
136 }
137 }
138 return null;
139 }
140
141 /** Delegates to forBodyOnly. */
142 public TypeData forBracedBodyOnly(BracedBody that, TypeData[] items_result) {
143 return forBodyOnly(that, items_result);
144 }
145
146 /** Delegates to forBodyOnly. */
147 public TypeData forUnbracedBodyOnly(UnbracedBody that, TypeData[] items_result) {
148 return forBodyOnly(that, items_result);
149 }
150
151
152 /** Make sure the enclosing method data is declared to return void. If it is not, give an error.
153 * @return the type the method is declared to return.
154 */
155 public TypeData forVoidReturnStatementOnly(VoidReturnStatement that) {
156 MethodData md = _bodyData.getMethodData();
157 if (md.getReturnType() != SymbolData.VOID_TYPE) {
158 _addError("Cannot return void when the method's expected return type is not void.", that);
159
160 // Return the correct type to allow type-checking to continue.
161 return md.getReturnType().getInstanceData();
162 }
163 return SymbolData.VOID_TYPE.getInstanceData();
164 }
165
166 /** Visit the value being returned to determine its type. Do the necessary bookkeeping and
167 * then delegate to forValueReturnStatementOnly.
168 */
169 public TypeData forValueReturnStatement(ValueReturnStatement that) {
170 ExpressionTypeChecker etc =
171 new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
172 TypeData valueRes = that.getValue().visit(etc);
173 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
174 return forValueReturnStatementOnly(that, valueRes);
175 }
176
177 /** Make sure that the enclosing method is declared to throw the same type as the return statement
178 * is trying to return. Also make sure that what is being returned is an instance of the type,
179 * not the type itself.
180 */
181 public TypeData forValueReturnStatementOnly(ValueReturnStatement that, TypeData valueRes) {
182 MethodData md = _bodyData.getMethodData();
183 SymbolData expected = md.getReturnType();
184
185 if (expected == null) {
186 // There was an error processing the method's return type; return the result type
187 return valueRes;
188 }
189
190 if (valueRes == null || ! assertFound(valueRes, that)) {
191 // There was an error parsing the return type, return the expected type.
192 return expected.getInstanceData();
193 }
194
195 if (valueRes != null && !valueRes.isInstanceType()) {
196 _addError("You cannot return a class or interface name. Perhaps you meant to say " + valueRes.getName() +
197 ".class or to create an instance", that);
198 return valueRes.getInstanceData();
199 }
200
201 if (expected == SymbolData.VOID_TYPE) {
202 _addError("Cannot return a value when the method's expected return type is void.", that);
203 // Return the correc type to allow type-checking to continue.
204 return SymbolData.VOID_TYPE.getInstanceData();
205 }
206 else if (!_isAssignableFrom(expected, valueRes.getSymbolData())) {
207 _addError("This method expected to return type: " + '"' + expected.getName() + '"'
208 + " but here returned type: " + '"' + valueRes.getName() + '"',
209 that);
210 }
211 return valueRes;
212 }
213
214 /** First, visit the condition expression of the for statement with a special visitor that
215 * makes sure no assignment is done.
216 * Then, visit the condition expression with the ExpressionTypeChecker which will do all the
217 * normal expression stuff.
218 * Then, visit the update and and code (block) of the for statement with this visitor.
219 * Be very careful about maintaing the various scopes here.
220 */
221 public TypeData forForStatement(ForStatement that) {
222 Boolean expOk = Boolean.TRUE;
223 if (that.getCondition() instanceof Expression) {
224 Expression exp = (Expression) that.getCondition();
225 // Assignment cannot be used in this expression
226 expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of a for-statement"));
227 }
228
229 LinkedList<VariableData> newVars = cloneVariableDataList(_vars);
230 BodyTypeChecker btc =
231 createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, newVars, _thrown);
232 final TypeData init_result = that.getInit().visit(btc);
233
234 ExpressionTypeChecker etc =
235 new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, btc._vars, _thrown);
236 final TypeData condition_result = that.getCondition().visit(etc);
237 btc.thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
238 final TypeData update_result = that.getUpdate().visit(btc);
239 final TypeData codeRes = that.getCode().visit(btc);
240
241 // Now, change any VariableDatas that were given a value in the ForStatement back to having been unassigned since
242 // its code is not necessarily executed.
243 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
244 if (expOk.booleanValue())
245 return forForStatementOnly(that, init_result, condition_result, update_result, codeRes);
246 else {return null;}
247 }
248
249
250 /* Make sure that the conditional expression has the right type. */
251 public TypeData forForStatementOnly(ForStatement that, TypeData init_result, TypeData condition_result,
252 TypeData update_result, TypeData codeRes) {
253 if (condition_result != null && assertFound(condition_result, that)) {
254 if (!condition_result.isInstanceType()) {
255 _addError("This for-statement's conditional expression must be a boolean value. Instead, it is a class or " +
256 "interface name", that);
257 }
258 else if (!condition_result.getSymbolData().isBooleanType(LanguageLevelConverter.OPT.javaVersion())) {
259 _addError("This for-statement's conditional expression must be a boolean value. Instead, its type is " +
260 condition_result.getName(), that);
261 }
262 }
263 return null;
264 }
265
266 /** First, visit the condition expression of the if statement with a special visitor that
267 * makes sure no assignment is done.
268 * Then, visit the condition expression with the ExpressionTypeChecker which will do all the
269 * normal expression stuff.
270 * Then, visit the body of the if statement with this visitor.
271 * Be very careful about maintaing the various scopes here.
272 */
273 public TypeData forIfThenStatement(IfThenStatement that) {
274 Boolean expOk = Boolean.TRUE;
275 if (that.getTestExpression() instanceof Expression) {
276 Expression exp = that.getTestExpression();
277 // Assignment cannot be used in this expression
278 expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of an if-then statement"));
279 }
280
281 // Update what has been assigned here with results from test expression, because any variables it sees or assigns
282 // will be visible in the rest of the body.
283 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
284 _vars, _thrown);
285 final TypeData testExpression_result = that.getTestExpression().visit(etc);
286 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
287
288 //Use a new visitor for the body of the then statement--it is a different lexical scope, so we want to track the
289 //variables seperately.
290 BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages,
291 cloneVariableDataList(_vars), _thrown);
292 final TypeData thenStatement_result = that.getThenStatement().visit(btc);
293
294 //Now, change any VariableDatas that were given a value in the ThenStatement back to having been unassigned
295 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
296
297 if (expOk.booleanValue()) {return forIfThenStatementOnly(that, testExpression_result, thenStatement_result);}
298 return null;
299 }
300
301 /* Make sure that the conditional expression has the right type. */
302 public TypeData forIfThenStatementOnly(IfThenStatement that, TypeData testExpression_result,
303 TypeData thenStatement_result) {
304 if (testExpression_result != null && assertFound(testExpression_result, that.getTestExpression())) {
305 if (!testExpression_result.isInstanceType()) {
306 _addError("This if-then-statement's conditional expression must be a boolean value. Instead, it is a class " +
307 "or interface name", that);
308 }
309 else if (!testExpression_result.getSymbolData().isBooleanType(LanguageLevelConverter.OPT.javaVersion())) {
310 _addError("This if-then-statement's conditional expression must be a boolean value. Instead, its type is " +
311 testExpression_result.getName(), that.getTestExpression());
312 }
313 }
314 return null;
315 }
316
317
318 /** First, visit the condition expression of the if-then-else statement with a special visitor that
319 * makes sure no assignment is done.
320 * Then, visit the condition expression with the ExpressionTypeChecker which will do all the
321 * normal expression stuff.
322 * Then, visit the body of the if statement and the else with this visitor.
323 * Be very careful about maintaing the various scopes here.
324 */
325 public TypeData forIfThenElseStatement(IfThenElseStatement that) {
326 Boolean expOk = Boolean.TRUE;
327 if (that.getTestExpression() instanceof Expression) {
328 Expression exp = that.getTestExpression();
329 // Assignment cannot be used in this expression
330 expOk = exp.visit(new NoAssignmentAllowedInExpression("the conditional expression of an if-then-else statement"));
331 }
332
333 // Update list of what has been assigned with one from test expression, because any variables it sees or assigns
334 // will be visible in the rest of the body.
335 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
336 _vars, _thrown);
337 final TypeData testExpression_result = that.getTestExpression().visit(etc);
338 thingsThatHaveBeenAssigned = etc.thingsThatHaveBeenAssigned;
339
340 //Use a new visitor for the body of the then statement--it is a different lexical scope, so we want to track the
341 //variables seperately.
342 BodyTypeChecker btcThen = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages,
343 cloneVariableDataList(_vars), _thrown);
344 final TypeData thenStatement_result = that.getThenStatement().visit(btcThen);
345 //Now, change any VariableDatas that were given a value in the ThenStatement back to having been unassigned
346 unassignVariableDatas(btcThen.thingsThatHaveBeenAssigned);
347
348
349 //Use a new visitor for the body of the else statement--it is a different lexical scope, so we want to track the
350 //variables seperately.
351 BodyTypeChecker btcElse = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages,
352 cloneVariableDataList(_vars), _thrown);
353 final TypeData elseStatement_result = that.getElseStatement().visit(btcElse);
354 //Now, change any VariableDatas that were given a value in the ElseStatement back to having been unassigned
355 unassignVariableDatas(btcElse.thingsThatHaveBeenAssigned);
356
357 //Now compare the two lists of VariableDatas, and reassign those that were assigned in both branches.
358 reassignVariableDatas(btcThen.thingsThatHaveBeenAssigned, btcElse.thingsThatHaveBeenAssigned);
359
360 if (expOk.booleanValue()) {return forIfThenElseStatementOnly(that, testExpression_result, thenStatement_result,
361 elseStatement_result);}
362 return null;
363 }
364
365 /** Make sure that the conditional expression has the right type, and if both branches of the
366 * if/else return, return a value the common super type of the two return types.
367 * We assume that thenStatement_result and elseStatement_result are InstanceDatas
368 */
369 public TypeData forIfThenElseStatementOnly(IfThenElseStatement that, TypeData testExpression_result,
370 TypeData thenStatement_result, TypeData elseStatement_result) {
371 if (testExpression_result != null && assertFound(testExpression_result, that.getTestExpression())) {
372 if (!testExpression_result.isInstanceType()) {
373 _addError("This if-then-else statement's conditional expression must be a boolean value. Instead, it is a " +
374 "class or interface name",
375 that);
376 }
377 else if (!testExpression_result.getSymbolData().isBooleanType(LanguageLevelConverter.OPT.javaVersion())) {
378 _addError("This if-then-else statement's conditional expression must be a boolean value. Instead, its type is "
379 + testExpression_result.getName(), that.getTestExpression());
380 }
381 }
382
383 if (testExpression_result == null || thenStatement_result == null || elseStatement_result == null) return null;
384
385 // We don't throw an error here because if the then and else branches return incompatible types,
386 // there must have already been an error thrown in forValueReturnStatementOnly
387 // that indicates that one of the return statements is returning the wrong type.
388 SymbolData result = getCommonSuperType(thenStatement_result.getSymbolData(), elseStatement_result.getSymbolData());
389 if (result==null) {return null;}
390 return result.getInstanceData();
391 }
392
393 /** First, visit the condition expression of the while statement with a special visitor that
394 * makes sure no assignment is done.
395 * Then, visit the condition expression with the ExpressionTypeChecker which will do all the
396 * normal expression stuff.
397 * Then, visit the body with this visitor.
398 * Be very careful about maintaing the various scopes here.
399 */
400 public TypeData forWhileStatement(WhileStatement that) {
401 Boolean expOk = Boolean.TRUE;
402 if (that.getCondition() instanceof Expression) {
403 Expression exp = that.getCondition();
404 // Assignment cannot be used in this expression
405 expOk = exp.visit(new NoAssignmentAllowedInExpression("the condition expression of a while statement"));
406 }
407
408 // Visit the condition expression with an expression type checker and
409 // then update list of what was assigned, because it will always be visited so it is in the same scope as the
410 // enclosing body.
411 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
412 _vars, _thrown);
413 final TypeData condition_result = that.getCondition().visit(etc);
414 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
415
416 //Use a new visitor for the body of the while statement--it is a different lexical scope, so we want to track the
417 //variables seperately.
418 BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages,
419 cloneVariableDataList(_vars), _thrown);
420 final TypeData codeRes = that.getCode().visit(btc);
421 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
422 if (expOk.booleanValue()) {return forWhileStatementOnly(that, condition_result, codeRes);}
423 return null;
424 }
425
426 /** Make sure that the condition statement of the while returns type boolean. */
427 public TypeData forWhileStatementOnly(WhileStatement that, TypeData condition_result, TypeData codeRes) {
428 if (condition_result != null && assertFound(condition_result, that.getCondition())) {
429 if (! condition_result.isInstanceType()) {
430 _addError("This while-statement's conditional expression must be a boolean value. Instead, it is a class or " +
431 "interface name", that);
432 }
433 else if (!condition_result.getSymbolData().isBooleanType(LanguageLevelConverter.OPT.javaVersion())) {
434 _addError("This while-statement's conditional expression must be a boolean value. Instead, its type is " +
435 condition_result.getName(), that.getCondition());
436 }
437 }
438 return null;
439 }
440
441 /** First, visit the body of the do statement with a body type checker.
442 * Then, visit the condition expression of the do statement with a special visitor that
443 * makes sure no assignment is done.
444 * Then, visit the condition expression with the ExpressionTypeChecker which will do all the
445 * normal expression stuff.
446 * Be very careful about maintaing the various scopes here.
447 */
448 public TypeData forDoStatement(DoStatement that) {
449 //Use a new visitor for the body of the do statement--it is a different lexical scope, so we want to track the
450 //variables seperately.
451 BodyTypeChecker btc = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages,
452 cloneVariableDataList(_vars), _thrown);
453 final TypeData codeRes = that.getCode().visit(btc);
454 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
455
456 Boolean expOk = Boolean.TRUE;
457 if (that.getCondition() instanceof Expression) {
458 Expression exp = that.getCondition();
459 // Assignment cannot be used in this expression
460 expOk = exp.visit(new NoAssignmentAllowedInExpression("the condition expression of a do statement"));
461 }
462
463 // Visit the condition statement with a new ExpressionTypeChecker, then update the thingsThatHaveBeenAssigned list, since it is in the same scope as the body.
464 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
465 final TypeData condition_result = that.getCondition().visit(etc);
466 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
467
468 if (expOk.booleanValue()) {return forDoStatementOnly(that, codeRes, condition_result);}
469 return null;
470
471 }
472
473
474 /**Make sure that the condition statement of the while returns type boolean. */
475 public TypeData forDoStatementOnly(DoStatement that, TypeData codeRes, TypeData condition_result) {
476 if (condition_result != null && assertFound(condition_result, that.getCondition())) {
477 if (!condition_result.isInstanceType()) {
478 _addError("This do-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", that.getCondition());
479 }
480 else if (!condition_result.getSymbolData().isBooleanType(LanguageLevelConverter.OPT.javaVersion())) {
481 _addError("This do-statement's conditional expression must be a boolean value. Instead, its type is " + condition_result.getName(), that.getCondition());
482 }
483 }
484 if (codeRes == null) {return null;}
485 return codeRes.getInstanceData();
486 }
487
488 /*Handle the switch statement(explained within the method)*/
489 public TypeData forSwitchStatement(SwitchStatement that) {
490 Expression exp = that.getTest();
491 //make sure no assignments are made in the switch stmt expression
492 exp.visit(new NoAssignmentAllowedInExpression("the switch expression of a switch statement"));
493
494 //Visit the test with this visitor, because it is in the scope of the method
495 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages, _vars, _thrown);
496 final TypeData testRes = that.getTest().visit(etc);
497 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
498
499 //The test result of a switch statement must be either an int or a char, according to the JLS.
500 if (testRes == null || !assertFound(testRes, exp)) {return null;}
501 if (!(_isAssignableFrom(SymbolData.INT_TYPE, testRes.getSymbolData()) || _isAssignableFrom(SymbolData.CHAR_TYPE, testRes.getSymbolData()))) {
502 _addError("The switch expression must be either an int or a char. You have used a " + testRes.getSymbolData().getName(), that.getTest());
503 }
504
505 final TypeData[] cases_result = makeArrayOfRetType(that.getCases().length);
506 BodyTypeChecker[] btcs = new BodyTypeChecker[that.getCases().length];
507 HashSet<Integer> labels = new HashSet<Integer>();
508 LinkedList<VariableData> variablesAssigned = new LinkedList<VariableData>();
509 boolean seenDefault = false;
510 boolean hadCaseReturn = false;
511
512 /**
513 * Loop over all the cases, type-checking them and then checking that no labels are duplicated
514 * and only one default statement is present.
515 */
516 for (int i = 0; i < that.getCases().length; i++) {
517 SwitchCase sc = that.getCases()[i];
518 btcs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
519 cases_result[i] = sc.visit(btcs[i]);
520 if (sc instanceof LabeledCase) {
521 LabeledCase lc = (LabeledCase) sc;
522 //Get the label, make sure it is an int or char, and then put it in our hash set.
523 Integer toCheck = null;
524 if (lc.getLabel() instanceof CharLiteral) {
525 toCheck = (int) ((CharLiteral) lc.getLabel()).getValue();
526 }
527 if (lc.getLabel() instanceof IntegerLiteral) {
528 toCheck = ((IntegerLiteral) lc.getLabel()).getValue();
529 }
530 if (toCheck != null) {
531 if (labels.contains(toCheck))
532 _addError("You cannot have two switch cases with the same label " + toCheck, lc.getLabel());
533 else labels.add(toCheck);
534 }
535 }
536 else {
537 if (seenDefault) _addError("A switch statement can only have one default case", sc);
538 seenDefault = true;
539 }
540
541 // Compare the variables assigned, and let variablesAssigned store the variables that have been assigned in every
542 // case that returns or breaks up to this one and are also assigned in this one
543 // if we're in the last case, we treat it as a break even though that may not happen explicitly
544
545 if (cases_result[i] != null || (i == cases_result.length - 1)) {
546 if (! hadCaseReturn) {
547 variablesAssigned = btcs[i].thingsThatHaveBeenAssigned;
548 hadCaseReturn = true;
549 }
550 else {
551 Iterator<VariableData> iter = variablesAssigned.iterator();
552 while (iter.hasNext()) {
553 if (!btcs[i].thingsThatHaveBeenAssigned.contains(iter.next())) {iter.remove();}
554 }
555 }
556 }
557
558 //Now, change any VariableDatas that were given a value in the case statement back to having been unassigned
559 unassignVariableDatas(btcs[i].thingsThatHaveBeenAssigned);
560 }
561
562 //Now assign a value to all variables that were assigned in each case branch, if there was a default case
563 if (seenDefault) {
564 for (VariableData vd : variablesAssigned) vd.gotValue();
565 }
566 return forSwitchStatementOnly(that, testRes, cases_result, seenDefault);
567 }
568
569 /** Here, we follow the following rules for determining what to return:
570 * If there is not a default block, the statement does not return.
571 * If the result from any of the blocks is NOT_FOUND, the statement does not return. (NOT_FOUND signifies that a
572 * break statement was seen). If the last block does not return, then the statement does not return.
573 */
574 public TypeData forSwitchStatementOnly(SwitchStatement that, TypeData testRes, TypeData[] cases_result,
575 boolean sawDefault) {
576
577 /**If we did not see a default block, this statement cannot be guaranteed to return*/
578 if (!sawDefault) return null;
579
580 /**If any of the blocks are NOT_FOUND, then the statement does not return.*/
581 for (int i = 0; i<cases_result.length; i++) {
582 if (cases_result[i] != null && cases_result[i].getSymbolData() == SymbolData.NOT_FOUND) {return null;}
583 }
584
585 /**If the last block does not return, then the statement also does not return. */
586 if (cases_result[cases_result.length-1] == null) return null;
587
588 return _bodyData.getMethodData().getReturnType().getInstanceData();
589 }
590
591
592 /** Make sure that the label for this LabeledCase is correct. The label must be a constant expression of type int or
593 * char. Then delegate to the super class to handle the braced body of the switch case. */
594 public TypeData forLabeledCase(LabeledCase that) {
595 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
596 _vars, _thrown);
597 final TypeData label_result = that.getLabel().visit(etc);
598 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
599 Expression exp = that.getLabel();
600
601 if (label_result == null || !assertFound(label_result, exp)) return null;
602
603 //we allow constant expressions of the form -5 or 5, but nothing else.
604 if (!(exp instanceof LexicalLiteral || exp instanceof NumericUnaryExpression &&
605 ((NumericUnaryExpression) exp).getValue() instanceof LexicalLiteral)) {
606 _addError("The labels of a switch statement must be constants. You are using a more complicated expression of" +
607 " type " + label_result.getSymbolData().getName(),
608 that.getLabel());
609 }
610 else if (!_isAssignableFrom(SymbolData.INT_TYPE, label_result.getSymbolData())) {
611 _addError("The labels of a switch statement must be constants of int or char type. You specified a constant of" +
612 " type " + label_result.getSymbolData().getName(),
613 that.getLabel());
614 }
615
616 return forSwitchCase(that);
617 }
618
619 /** Delegate handling this default case to its superclass. */
620 public TypeData forDefaultCase(DefaultCase that) { return forSwitchCase(that); }
621
622 /** Visit the Braced Body of this SwitchCase, and return the result. */
623 public TypeData forSwitchCase(SwitchCase that) {
624 final TypeData codeRes = that.getCode().visit(this);
625
626 //If the case falls through (i.e. returns null) but has some statements in it, then there is an error.
627 //We only want to allow fall through for multiple labels, of the form:
628 /** 'a':
629 * 'b':
630 * return 5;
631 */
632
633 if (codeRes == null && that.getCode().getStatements().length > 0) {
634 _addError("You must end a non-empty switch case with a break or return statement at the Advanced level", that);
635 }
636 return codeRes;
637 }
638
639 /* Visit the block, and update with what it assigns. Return the result of visiting the block. */
640 public TypeData forBlock(Block that) {
641 BlockData bd = _bodyData.getNextBlock();
642 if (bd == null)
643 throw new RuntimeException("Internal Program Error: Enclosing body does not contain this block." +
644 " Please report this bug");
645
646 BodyTypeChecker btc = createANewInstanceOfMe(bd, _file, _package, _importedFiles, _importedPackages,
647 cloneVariableDataList(_vars), _thrown);
648 TypeData statements_result = that.getStatements().visit(btc);
649 thingsThatHaveBeenAssigned.addAll(btc.thingsThatHaveBeenAssigned);
650 return statements_result;
651 }
652
653
654 /*Try to resolve the type and make sure it can be referenced from this context (i.e. is accessible).*/
655 public TypeData forTypeOnly(Type that) {
656 Data sd = getSymbolData(that.getName(), _data, that);
657 while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
658 if (!checkAccess(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _bodyData.getSymbolData(), "class")) {
659 return null;
660 }
661 sd = sd.getOuterData();
662 }
663 return null;
664 }
665
666 /** Makes sure that no super class of any exception is caught before the current exception's catch block. */
667 protected void checkDuplicateExceptions(TryCatchStatement that) {
668 // Make sure that the user isn't throwing duplicate exceptions
669 LinkedList<SymbolData> catchBlockExceptions = new LinkedList<SymbolData>();
670 CatchBlock[] catchBlocks = that.getCatchBlocks();
671 for (int i = 0; i < catchBlocks.length; i++) {
672 catchBlockExceptions.addLast(getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(),
673 _data, catchBlocks[i].getException()));
674 }
675 for (int i = 0; i < catchBlockExceptions.size(); i++) {
676 for (int j = i+1; j < catchBlockExceptions.size(); j++) {
677 if (catchBlockExceptions.get(j) != null && catchBlockExceptions.get(j).isSubClassOf(catchBlockExceptions.get(i))) {
678 _addError("Exception " + catchBlockExceptions.get(j).getName() +
679 " has already been caught", catchBlocks[j].getException());
680 }
681 }
682 }
683 }
684
685 /** Check if the two given SymbolDatas have a common super type. If so, return it, else return null. */
686 protected SymbolData getCommonSuperType(SymbolData s1, SymbolData s2) {
687 if ((s1 == null) && (s2 == null)) {
688 return null;
689 }
690
691 if (s1 == SymbolData.NOT_FOUND && s2 != null) {return SymbolData.NOT_FOUND;}
692 if (s2 == SymbolData.NOT_FOUND && s1 != null) {return SymbolData.NOT_FOUND;}
693
694 if (s1 == null && s1 != SymbolData.NOT_FOUND) { return s2; }
695 if (s2 == null && s1 != SymbolData.NOT_FOUND) {return s1;}
696 if (s1==null || s2==null) {return null;}
697 if (s1 == SymbolData.EXCEPTION) { return s2; }
698 if (s2 == SymbolData.EXCEPTION) { return s1; }
699
700 // See if s1 and s2 have a common super class.
701 SymbolData sd = getCommonSuperTypeBaseCase(s1, s2);
702 if (sd != null ) { return sd; }
703 sd = getCommonSuperTypeBaseCase(s2, s1);
704 if (sd != null) { return sd; }
705
706 //If s1's superClass is null, then we have gone all the way through the superclass hierarchy without finding a matching class.
707 if (s1.getSuperClass() == null) {
708 //return null;
709 //since we know that Object should be the super class of everything, return Object.
710 return getSymbolData("java.lang.Object", _data, new NullLiteral(SourceInfo.NONE));
711 }
712
713 // Recur on the super class chain.
714 sd = getCommonSuperType(s1.getSuperClass(), s2);
715 if (sd != null) {
716 return sd;
717 }
718
719 // Recur on each interface.
720 for (SymbolData currSd : s1.getInterfaces()) {
721 sd = getCommonSuperType(currSd, s2);
722 if (sd != null) {
723 return sd;
724 }
725 }
726 return null;
727 }
728
729
730 /** @return true if the symbol data is the generic SymbolData.EXCEPTIOn class or if it extends java.lang.Throwable*/
731 protected boolean isException(SymbolData sd) {
732 return sd == SymbolData.EXCEPTION ||
733 sd.isSubClassOf(getSymbolData("java.lang.Throwable", new NullLiteral(SourceInfo.NONE), false, false));
734 }
735
736 /** Returns the least restrictive type returned by the try block and catch blocks. Returns null
737 * if this try-catch statement doesn't necessarily return a value.
738 */
739 protected InstanceData tryCatchLeastRestrictiveType(InstanceData tryBlockRes, InstanceData[] catchBlocksRes,
740 InstanceData finallyBlock_result) {
741 // Return the common superclass or null if there exists a block that doesn't return a value(except the finally block)
742 if (tryBlockRes == null || tryBlockRes == SymbolData.NOT_FOUND.getInstanceData())
743 return finallyBlock_result;
744 TypeData leastRestrictiveType = tryBlockRes;
745 for (int i = 0; i < catchBlocksRes.length; i++) {
746 if (catchBlocksRes[i] == null) return finallyBlock_result;
747 if (catchBlocksRes[i] != SymbolData.NOT_FOUND.getInstanceData() &&
748 _isAssignableFrom(catchBlocksRes[i].getSymbolData(), leastRestrictiveType.getSymbolData())) {
749 leastRestrictiveType = catchBlocksRes[i];
750 }
751 }
752
753 SymbolData result;
754 if (leastRestrictiveType == null && finallyBlock_result == null) return null;
755 else if (leastRestrictiveType == null) result = getCommonSuperType(null, finallyBlock_result.getSymbolData());
756 else if (finallyBlock_result == null) result = getCommonSuperType(leastRestrictiveType.getSymbolData(), null);
757 else result = getCommonSuperType(leastRestrictiveType.getSymbolData(), finallyBlock_result.getSymbolData());
758
759 if (result != null) return result.getInstanceData();
760 return null;
761 }
762
763 /** Return true if the Exception is unchecked, and false otherwise.
764 * An exception is unchecked if it does not extend either java.lang.RuntimeException or java.lang.Error,
765 * and is not declared to be thrown by the enclosing method.
766 * @param sd The SymbolData of the Exception we are checking.
767 * @param that The JExpression passed to getSymbolData for error purposes.
768 */
769 public boolean isUncaughtCheckedException(SymbolData sd, JExpression that) {
770 if (isCheckedException(sd, that)) {
771 MethodData md = _bodyData.getMethodData();
772 for (int i = 0; i<md.getThrown().length; i++) {
773 //If the Exception matches an exception declared to be thrown by the enclosing method, it is not unchecked.
774 if (sd.isSubClassOf(getSymbolData(md.getThrown()[i], _data, that))) {return false;}
775 }
776 return true;
777 }
778 return false;
779 }
780
781 /** Make sure that every exception that is caught could have been thrown in the try statement */
782 protected void makeSureCaughtStuffWasThrown(TryCatchStatement that, SymbolData[] caught_array, LinkedList<Pair<SymbolData, JExpression>> thrown) {
783 // Make sure every Exception that is caught could actually be thrown
784 for (int i = 0; i < caught_array.length; i++) {
785 SymbolData currCaughtSD = caught_array[i];
786 // System.err.println("currCaughtSD = " + currCaughtSD + " isChecked = " + isCheckedException(currCaughtSD, that));
787 boolean foundThrownException = false;
788 if (isCheckedException(currCaughtSD, that) &&
789 ! currCaughtSD.getName().equals("java.lang.Exception") &&
790 ! currCaughtSD.getName().equals("java.lang.Throwable")) {
791 // System.err.println("Checking thrown");
792 for (Pair<SymbolData, JExpression> p : thrown) {
793 SymbolData sd = p.getFirst();
794 if (sd.isSubClassOf(currCaughtSD)) {
795 foundThrownException = true;
796 }
797 }
798 if (!foundThrownException) {
799 // System.err.println("Calling _addError for " + currCaughtSD);
800 _addError("The exception " + currCaughtSD.getName() +
801 " is never thrown in the body of the corresponding try block",
802 that.getCatchBlocks()[i]);
803 }
804 }
805 }
806 }
807
808 /** Make sure that every Exception in thrown is either in caught or in the list of what can be thrown from where we are.
809 * Also make sure that every Exception that is declared to be thrown or caught is actually thrown.
810 * @param that The TryCatchStatement we are currently working with
811 * @param caught_array The SymbolData[] of exceptions that are explicitely caught.
812 * @param thrown The LinkedList of SymbolData of exceptions that are thrown. This will be modified.
813 */
814 protected void compareThrownAndCaught(TryCatchStatement that, SymbolData[] caught_array,
815 LinkedList<Pair<SymbolData, JExpression>> thrown) {
816 LinkedList<SymbolData> caught = new LinkedList<SymbolData>();
817 for (int i = 0; i<caught_array.length; i++) {
818 caught.addLast(caught_array[i]);
819 }
820
821 //Make sure that every Exception in thrown is either caught or in the list of what can be thrown
822 for (Pair<SymbolData, JExpression> p : thrown) {
823 SymbolData sd = p.getFirst();
824 JExpression j = p.getSecond();
825 if (isUncaughtCheckedException(sd, j)) {
826 boolean foundCatchBlock = false;
827 for (SymbolData currCaughtSD : caught) {
828 if (sd.isSubClassOf(currCaughtSD)) {
829 foundCatchBlock = true;
830 }
831 }
832 // Check if this exception is a checked exception and is not declared to be thrown by the enclosing method
833 //This is a checked exception. It should have been caught.
834 if (! foundCatchBlock) {
835 handleUncheckedException(sd, j);
836 }
837 }
838 }
839
840 makeSureCaughtStuffWasThrown(that, caught_array, thrown);
841 }
842
843 /** Assumes that tryBlockRes, catchBlocksRes, and finallyBlock_result are InstanceDatas. */
844 public TypeData forTryCatchFinallyStatementOnly(TryCatchFinallyStatement that, TypeData tryBlockRes, TypeData[] catchBlocksRes, TypeData finallyBlock_result) {
845 checkDuplicateExceptions(that);
846
847 //we know ids are instance datas, but we have to do this to cast them properly.
848 InstanceData[] ids = new InstanceData[catchBlocksRes.length];
849 for (int i = 0; i<ids.length; i++) {
850 if (catchBlocksRes[i] != null) {
851 ids[i]=catchBlocksRes[i].getInstanceData();
852 }
853 else {ids[i]=null;}
854 }
855
856 /**Make sure null pointer exceptions don't happen.*/
857 if (tryBlockRes == null && finallyBlock_result==null) {return tryCatchLeastRestrictiveType(null, ids, null);}
858 if (tryBlockRes == null) {return tryCatchLeastRestrictiveType(null, ids, finallyBlock_result.getInstanceData());}
859 if (finallyBlock_result == null) {return tryCatchLeastRestrictiveType(tryBlockRes.getInstanceData(), ids, null);}
860
861 return tryCatchLeastRestrictiveType(tryBlockRes.getInstanceData(), ids, finallyBlock_result.getInstanceData());
862 }
863
864 /*Visit the try block, catch blocks, and finally block. Add any exceptions that are not caught to thrown.*/
865 public TypeData forTryCatchFinallyStatement(TryCatchFinallyStatement that) {
866
867 BodyTypeChecker btc = new TryCatchBodyTypeChecker(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), new LinkedList<Pair<SymbolData, JExpression>>());
868 final TypeData tryBlockRes = that.getTryBlock().visit(btc);
869
870 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
871
872 BodyTypeChecker[] catchTCs = new BodyTypeChecker[that.getCatchBlocks().length];
873 LinkedList<LinkedList<VariableData>> catchVars = new LinkedList<LinkedList<VariableData>>();
874 CatchBlock[] catchBlocks = that.getCatchBlocks();
875 final TypeData[] catchBlocksRes = makeArrayOfRetType(catchBlocks.length);
876 final SymbolData[] caughtExceptions = new SymbolData[catchBlocks.length];
877
878 for (int i = 0; i < catchBlocks.length; i++) {
879 catchTCs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
880 catchBlocksRes[i] = catchBlocks[i].visit(catchTCs[i]);
881 unassignVariableDatas(catchTCs[i].thingsThatHaveBeenAssigned);
882 catchVars.addLast(catchTCs[i].thingsThatHaveBeenAssigned);
883 caughtExceptions[i] = getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(), _data, catchBlocks[i]);
884 }
885
886 BodyTypeChecker btcFinally = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
887 TypeData finallyBlock_result = that.getFinallyBlock().visit(btcFinally);
888 //leave anything that was assigned in the finally block assigned. It is always visited.
889
890 //Give values to anything assigned in the try and catch blocks.
891 reassignLotsaVariableDatas(btc.thingsThatHaveBeenAssigned, catchVars);
892
893 //If the finallyBlock didn't end abruptly, need to check thrown and caught
894
895 if (finallyBlock_result == null) {
896 compareThrownAndCaught(that, caughtExceptions, btc._thrown);
897 }
898
899 else { /**It is like thrown is empty--any exceptions in it don't need to be caught.*/
900 _thrown = new LinkedList<Pair<SymbolData, JExpression>>();
901 }
902
903 if (finallyBlock_result == SymbolData.NOT_FOUND.getInstanceData()) {
904 finallyBlock_result = null;
905 }
906
907 // Add the exceptions thrown by btc to our thrown list.
908 if (this instanceof TryCatchBodyTypeChecker) {
909 _thrown.addAll(btc._thrown);
910 }
911
912 return forTryCatchFinallyStatementOnly(that, tryBlockRes, catchBlocksRes, finallyBlock_result);
913 }
914
915
916 /** Resolves the type of the exception, and visits the body, making sure the exception variable
917 * is in scope.
918 */
919 public TypeData forCatchBlock(CatchBlock that) {
920 VariableDeclarator dec = that.getException().getDeclarator();
921 SymbolData exception_result = getSymbolData(dec.getType().getName(), _data, dec.getType());
922
923 BlockData bd = _bodyData.getNextBlock();
924 if (bd == null) { throw new RuntimeException("Internal Program Error: Enclosing body does not contain this block. Please report this bug"); }
925 VariableData vd = bd.getVar(dec.getName().getText());
926 if (vd == null) { throw new RuntimeException("Internal Program Error: Catch block does not contain its exception variable. Please report this bug"); }
927 LinkedList<VariableData> newVars = cloneVariableDataList(_vars);
928 newVars.addLast(vd);
929 BodyTypeChecker btc = createANewInstanceOfMe(bd, _file, _package, _importedFiles, _importedPackages, newVars, _thrown);
930 TypeData block_result = that.getBlock().getStatements().visit(btc);
931 thingsThatHaveBeenAssigned.addAll(btc.thingsThatHaveBeenAssigned);
932 return forCatchBlockOnly(that, exception_result, block_result);
933 }
934
935 /*Return the result of visiting the body*/
936 public TypeData forCatchBlockOnly(CatchBlock that, SymbolData exception_result, TypeData block_result) {
937 return block_result;
938 }
939
940 /**Assumes that tryBlockRes, catchBlocksRes, and finallyBlock_result are InstanceDatas*/
941 public TypeData forNormalTryCatchStatementOnly(NormalTryCatchStatement that, TypeData tryBlockRes, TypeData[] catchBlocksRes) {
942 checkDuplicateExceptions(that);
943 InstanceData[] ids = new InstanceData[catchBlocksRes.length];
944 for (int i = 0; i<catchBlocksRes.length; i++) {
945 ids[i]=(InstanceData) catchBlocksRes[i];
946
947 }
948
949 return tryCatchLeastRestrictiveType((InstanceData) tryBlockRes, ids, null);
950 }
951
952 /*no finally block*/
953 public TypeData forNormalTryCatchStatement(NormalTryCatchStatement that) {
954 BodyTypeChecker btc = new TryCatchBodyTypeChecker(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), new LinkedList<Pair<SymbolData, JExpression>>());
955 final TypeData tryBlockRes = that.getTryBlock().visit(btc);
956 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
957
958 LinkedList<LinkedList<VariableData>> catchVars = new LinkedList<LinkedList<VariableData>>();
959 BodyTypeChecker[] catchTCs = new BodyTypeChecker[that.getCatchBlocks().length];
960
961 CatchBlock[] catchBlocks = that.getCatchBlocks();
962 final TypeData[] catchBlocksRes = makeArrayOfRetType(catchBlocks.length);
963 final SymbolData[] caughtExceptions = new SymbolData[catchBlocks.length];
964 for (int i = 0; i < catchBlocks.length; i++) {
965 catchTCs[i] = createANewInstanceOfMe(_bodyData, _file, _package, _importedFiles, _importedPackages, cloneVariableDataList(_vars), _thrown);
966 catchBlocksRes[i] = catchBlocks[i].visit(catchTCs[i]);
967 unassignVariableDatas(catchTCs[i].thingsThatHaveBeenAssigned);
968 catchVars.addLast(catchTCs[i].thingsThatHaveBeenAssigned);
969 caughtExceptions[i] = getSymbolData(catchBlocks[i].getException().getDeclarator().getType().getName(), _data, catchBlocks[i]);
970 }
971
972 //Give values to anything assigned in the try and catch blocks.
973 reassignLotsaVariableDatas(btc.thingsThatHaveBeenAssigned, catchVars);
974
975 compareThrownAndCaught(that, caughtExceptions, btc._thrown);
976
977 // Add the exceptions thrown by btc to our thrown list.
978 if (this instanceof TryCatchBodyTypeChecker) {
979 _thrown.addAll(btc._thrown);
980 }
981 return forNormalTryCatchStatementOnly(that, tryBlockRes, catchBlocksRes);
982 }
983
984
985 /* A special visitor that does not allow assignment in any expressions*/
986 private class NoAssignmentAllowedInExpression extends JExpressionIFAbstractVisitor<Boolean> {
987 String _location;
988 private NoAssignmentAllowedInExpression(String location) { _location = location; }
989
990 /** Most expressions do not involve assignment. */
991 public Boolean defaultCase(JExpressionIF that) { return Boolean.TRUE; }
992
993 /* Throw an appropriate error*/
994 public Boolean forIncrementExpression(IncrementExpression that) {
995 _addError("You cannot use an increment or decrement expression in " + _location + " at any language level", that);
996 return Boolean.FALSE;
997 }
998
999 /*Throw an appropriate error*/
1000 public Boolean forAssignmentExpression(AssignmentExpression that) {
1001 _addError("You cannot use an assignment expression in " + _location + " at any language level", that);
1002 return Boolean.FALSE;
1003 }
1004
1005 /*Throw an appropriate error*/
1006 public Boolean forSimpleAssignmentExpression(SimpleAssignmentExpression that) {
1007 _addError("You cannot use an assignment expression in " + _location + " at any language level" + ". Perhaps you meant to compare two values with '=='", that);
1008 return Boolean.FALSE;
1009 }
1010
1011 }
1012
1013 /** Test the methods in the above class. */
1014 public static class BodyTypeCheckerTest extends TestCase {
1015
1016 private BodyTypeChecker _bbtc;
1017
1018 private BodyData _bd1;
1019 private BodyData _bd2;
1020
1021 private SymbolData _sd1;
1022 private SymbolData _sd2;
1023 private SymbolData _sd3;
1024 private SymbolData _sd4;
1025 private SymbolData _sd5;
1026 private SymbolData _sd6;
1027 private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
1028 private ModifiersAndVisibility _protectedMav =
1029 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
1030 private ModifiersAndVisibility _privateMav =
1031 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
1032 private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
1033 private ModifiersAndVisibility _abstractMav =
1034 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
1035 private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
1036
1037 public BodyTypeCheckerTest() { this(""); }
1038 public BodyTypeCheckerTest(String name) { super(name); }
1039
1040 public void setUp() {
1041 _sd1 = new SymbolData("i.like.monkey");
1042 _sd2 = new SymbolData("i.like.giraffe");
1043 _sd3 = new SymbolData("zebra");
1044 _sd4 = new SymbolData("u.like.emu");
1045 _sd5 = new SymbolData("");
1046 _sd6 = new SymbolData("cebu");
1047
1048 _bd1 = new MethodData("methodName1",
1049 _packageMav,
1050 new TypeParameter[0],
1051 SymbolData.INT_TYPE,
1052 new VariableData[] { new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null), new VariableData(SymbolData.BOOLEAN_TYPE) },
1053 new String[0],
1054 _sd1,
1055 null); // no SourceInfo
1056 ((MethodData)_bd1).getParams()[0].setEnclosingData(_bd1);
1057 ((MethodData)_bd1).getParams()[1].setEnclosingData(_bd1);
1058
1059 _bd2 = new MethodData("methodName2",
1060 _packageMav,
1061 new TypeParameter[0],
1062 SymbolData.VOID_TYPE,
1063 new VariableData[] { new VariableData(SymbolData.INT_TYPE) },
1064 new String[0],
1065 _sd1,
1066 null); // no SourceInfo);
1067 ((MethodData)_bd2).getParams()[0].setEnclosingData(_bd2);
1068
1069 errors = new LinkedList<Pair<String, JExpressionIF>>();
1070 LanguageLevelConverter.symbolTable.clear();
1071 LanguageLevelConverter._newSDs.clear();
1072 _bd1.addEnclosingData(_sd1);
1073 _bd1.addVars(((MethodData)_bd1).getParams());
1074 _bd2.addVars(((MethodData)_bd2).getParams());
1075 _bbtc = new BodyTypeChecker(_bd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(),
1076 new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData,JExpression>>());
1077 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
1078 _bbtc._importedPackages.addFirst("java.lang");
1079 }
1080
1081 public void testForUninitializedVariableDeclaratorOnly() {
1082 VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _bd1);
1083 _bd1.addVar(vd1);
1084 UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE,
1085 new PrimitiveType(SourceInfo.NONE, "int"),
1086 new Word(SourceInfo.NONE, "Mojo"));
1087 uvd.visit(_bbtc);
1088 assertTrue("_vars should contain Mojo.", _bbtc._vars.contains(vd1));
1089 }
1090
1091 public void testForInitializedVariableDeclaratorOnly() {
1092 _bbtc.symbolTable.put("int", SymbolData.INT_TYPE);
1093 VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, true, _bd1);
1094 _bd1.addVar(vd1);
1095 InitializedVariableDeclarator ivd = new InitializedVariableDeclarator(SourceInfo.NONE,
1096 new PrimitiveType(SourceInfo.NONE, "int"),
1097 new Word(SourceInfo.NONE, "Mojo"),
1098 new IntegerLiteral(SourceInfo.NONE, 1));
1099 ivd.visit(_bbtc);
1100 assertEquals("There should be no errors.", 0, errors.size());
1101 assertTrue("_vars should contain Mojo.", _bbtc._vars.contains(vd1));
1102 ivd = new InitializedVariableDeclarator(SourceInfo.NONE,
1103 new PrimitiveType(SourceInfo.NONE, "int"),
1104 new Word(SourceInfo.NONE, "Santa's Little Helper"),
1105 new IntegerLiteral(SourceInfo.NONE, 1));
1106 try {
1107 ivd.visit(_bbtc);
1108 fail("Should have thrown a RuntimeException because there's no field named Santa's Little Helper.");
1109 }
1110 catch (RuntimeException re) {
1111 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());
1112 }
1113 }
1114
1115 public void testForBracedBodyOnly() {
1116 // Test one that works.
1117 BracedBody bb1 = new BracedBody(SourceInfo.NONE,
1118 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1119 new IntegerLiteral(SourceInfo.NONE, 1))});
1120 TypeData sd = bb1.visit(_bbtc);
1121 assertEquals("There should be no errors", 0, errors.size());
1122 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
1123 BracedBody bb2 = new BracedBody(SourceInfo.NONE,
1124 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1125 new CharLiteral(SourceInfo.NONE, 'e'))});
1126 //test another one that works.
1127 sd = bb2.visit(_bbtc);
1128 assertEquals("There should be no errors", 0, errors.size());
1129 assertEquals("Should return char type", SymbolData.CHAR_TYPE.getInstanceData(), sd);
1130 BracedBody bb3 = new BracedBody(SourceInfo.NONE,
1131 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1132 new IntegerLiteral(SourceInfo.NONE, 1)),
1133 new ValueReturnStatement(SourceInfo.NONE,
1134 new CharLiteral(SourceInfo.NONE, 'e'))});
1135 //test one that should throw an error: unreachable return statement.
1136 sd = bb3.visit(_bbtc);
1137 assertEquals("There should be one error", 1, errors.size());
1138 assertEquals("The error message should be correct", "Unreachable statement.", errors.get(0).getFirst());
1139 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
1140
1141 BracedBody bb4 = new BracedBody(SourceInfo.NONE,
1142 new BodyItemI[0]);
1143 //test empty body. should return null.
1144 sd = bb4.visit(_bbtc);
1145 assertEquals("There should still be one error", 1, errors.size());
1146 assertEquals("The error message should still be be correct", "Unreachable statement.", errors.get(0).getFirst());
1147 assertEquals("Should return null", null, sd);
1148 }
1149
1150 public void testForVoidReturnStatementOnly() {
1151 _bbtc._bodyData = _bd2; //this body data has a void return type
1152
1153 //test one that works
1154 BracedBody bb1 = new BracedBody(SourceInfo.NONE,
1155 new BodyItemI[] { new VoidReturnStatement(SourceInfo.NONE)});
1156
1157 TypeData sd = bb1.visit(_bbtc);
1158
1159 assertEquals("There should be no errors.", 0, errors.size());
1160 assertEquals("Should return void type.", SymbolData.VOID_TYPE.getInstanceData(), sd);
1161
1162 //test with a method that doesn't return void.
1163 _bbtc._bodyData = _bd1;
1164 sd = bb1.visit(_bbtc);
1165 assertEquals("There should be one error", 1, errors.size());
1166 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
1167 assertEquals("Error message should be correct", "Cannot return void when the method's expected return type is not void.", errors.get(0).getFirst());
1168
1169 }
1170
1171 public void testforValueReturnStatementOnly() {
1172 //value return statement returns something that is not a subclass the method return type
1173 BracedBody bb1 = new BracedBody(SourceInfo.NONE,
1174 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1175 new BooleanLiteral(SourceInfo.NONE, true))});
1176 TypeData sd = bb1.visit(_bbtc);
1177 assertEquals("There should be one error", 1, errors.size());
1178 assertEquals("Should return boolean type", SymbolData.BOOLEAN_TYPE.getInstanceData(), sd);
1179 assertEquals("Error message should be correct",
1180 "This method expected to return type: \"int\" but here returned type: \"boolean\"", errors.get(0).getFirst());
1181
1182 //value return statement returns something that is assignable from the method return type
1183 BracedBody bb2 = new BracedBody(SourceInfo.NONE,
1184 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1185 new CharLiteral(SourceInfo.NONE, 'c'))});
1186 sd = bb2.visit(_bbtc);
1187 assertEquals("There should be still be one error", 1, errors.size());
1188 assertEquals("Should return char type", SymbolData.CHAR_TYPE.getInstanceData(), sd);
1189 assertEquals("Error message should still be correct", "This method expected to return type: \"int\" but here returned type: \"boolean\"", errors.get(0).getFirst());
1190
1191
1192 //method returns void
1193 _bbtc._bodyData = _bd2; //this body data has a void return type
1194
1195 BracedBody bb3 = new BracedBody(SourceInfo.NONE,
1196 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1197 new IntegerLiteral(SourceInfo.NONE, 1))});
1198
1199 sd = bb3.visit(_bbtc);
1200 assertEquals("There should be two errors", 2, errors.size());
1201 assertEquals("Should return void type", SymbolData.VOID_TYPE.getInstanceData(), sd);
1202 assertEquals("Error message should be correct", "Cannot return a value when the method's expected return type is void.", errors.get(1).getFirst());
1203
1204 // Test where the return value is a class name.
1205 BracedBody bb4 = new BracedBody(SourceInfo.NONE,
1206 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1207 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "int")))});
1208
1209 sd = bb4.visit(_bbtc);
1210 assertEquals("There should be 3 errors", 3, errors.size());
1211 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
1212 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());
1213
1214 }
1215
1216 public void testForIfThenElseStatementOnly() {
1217 //test if the expression is not of boolean type
1218 IfThenElseStatement ites1 = new IfThenElseStatement(SourceInfo.NONE,
1219 new IntegerLiteral(SourceInfo.NONE, 1),
1220 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
1221 new ValueReturnStatement(SourceInfo.NONE, new CharLiteral(SourceInfo.NONE, 'j')));
1222
1223 TypeData sd = ites1.visit(_bbtc);
1224 assertEquals("There should be one error", 1, errors.size());
1225 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());
1226
1227 assertEquals("Should return integer type", SymbolData.INT_TYPE.getInstanceData(), sd);
1228
1229
1230 //test if the branches do not return subtypes of each other
1231 IfThenElseStatement ites2 = new IfThenElseStatement(SourceInfo.NONE,
1232 new BooleanLiteral(SourceInfo.NONE, true),
1233 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
1234 new ValueReturnStatement(SourceInfo.NONE, new BooleanLiteral(SourceInfo.NONE, true)));
1235
1236 sd = ites2.visit(_bbtc);
1237 assertEquals("There should be two errors", 2, errors.size());
1238
1239 assertEquals("Should return Object type", "java.lang.Object", sd.getName());
1240 assertEquals("Error message should be correct",
1241 "This method expected to return type: \"int\" but here returned type: \"boolean\"",
1242 errors.get(1).getFirst());
1243
1244 //test if they do return subtypes of each other
1245 IfThenElseStatement ites3 = new IfThenElseStatement(SourceInfo.NONE,
1246 new BooleanLiteral(SourceInfo.NONE, true),
1247 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
1248 new ValueReturnStatement(SourceInfo.NONE, new CharLiteral(SourceInfo.NONE, 'f')));
1249
1250 sd = ites3.visit(_bbtc);
1251 assertEquals("There should still be two errors", 2, errors.size());
1252 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), sd);
1253
1254 //test if neither branch returns
1255 IfThenElseStatement ites4 = new IfThenElseStatement(SourceInfo.NONE,
1256 new BooleanLiteral(SourceInfo.NONE, true),
1257 new EmptyStatement(SourceInfo.NONE),
1258 new EmptyStatement(SourceInfo.NONE));
1259
1260 sd = ites4.visit(_bbtc);
1261 assertEquals("There should still be two errors", 2, errors.size());
1262 assertEquals("Should return null type", null, sd);
1263
1264 //test if only one branch returns
1265 IfThenElseStatement ites5 = new IfThenElseStatement(SourceInfo.NONE,
1266 new BooleanLiteral(SourceInfo.NONE, true),
1267 new EmptyStatement(SourceInfo.NONE),
1268 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 3)));
1269
1270 sd = ites5.visit(_bbtc);
1271 assertEquals("There should still be two errors", 2, errors.size());
1272 assertEquals("Should return null type", null, sd);
1273
1274
1275 // Test for the word "boolean" as the condition.
1276 IfThenElseStatement ites6 = new IfThenElseStatement(SourceInfo.NONE,
1277 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "boolean")),
1278 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
1279 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)));
1280
1281 sd = ites6.visit(_bbtc);
1282 assertEquals("There should be 3 errors", 3, errors.size());
1283
1284 assertEquals("Should return Integer type", SymbolData.INT_TYPE.getInstanceData(), sd);
1285 assertEquals("Error message should be correct",
1286 "This if-then-else statement's conditional expression must be a boolean value. Instead, it is a class or interface name",
1287 errors.get(2).getFirst());
1288 }
1289
1290 public void testForBlock() {
1291 //Check that a block can reference fields in its enclosing method.
1292 Block b = new Block(SourceInfo.NONE,
1293 new BracedBody(SourceInfo.NONE,
1294 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
1295 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")))}));
1296 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1297 LinkedList<VariableData> vars = new LinkedList<VariableData>();
1298 vars.addLast(new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, _bd1));
1299 _bbtc._vars = vars;
1300 TypeData sd = b.visit(_bbtc);
1301 assertEquals("There should not be any errors.", 0, errors.size());
1302 assertEquals("Should return int type.", SymbolData.INT_TYPE.getInstanceData(), sd);
1303 }
1304
1305 public void testForIfThenStatementOnly() {
1306 SymbolData sd1 = SymbolData.BOOLEAN_TYPE;
1307 SymbolData sd2 = SymbolData.INT_TYPE;
1308
1309 IfThenStatement its = new IfThenStatement(SourceInfo.NONE,
1310 new NullLiteral(SourceInfo.NONE),
1311 new EmptyStatement(SourceInfo.NONE));
1312
1313
1314 //test a correct condition type
1315 assertEquals("sd1 is boolean type, so should not add error. Returns null.", null,
1316 _bbtc.forIfThenStatementOnly(its, sd1.getInstanceData(), null));
1317 assertEquals("No errors should have been added", 0, errors.size());
1318
1319 //test an incorrect condition type
1320 assertEquals("sd2 is not boolean type, so should add error. Returns null.", null,
1321 _bbtc.forIfThenStatementOnly(its, sd2.getInstanceData(), null));
1322 assertEquals("Should now be one error.", 1, errors.size());
1323 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());
1324
1325 //test "bool" as a condition
1326 assertEquals("sd1 is not an instance, so should add error. Returns null.", null,
1327 _bbtc.forIfThenStatementOnly(its, sd1, null));
1328 assertEquals("Should now be 2 errors.", 2, errors.size());
1329 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());
1330 }
1331
1332 public void testForIfThenStatement() {
1333 //Test that the proper variable assignment happens.
1334 //here, a variable is only assigned in the then branch, so it should not be set after it returns.
1335 Expression te = new LessThanExpression(SourceInfo.NONE,
1336 new SimpleNameReference(SourceInfo.NONE,
1337 new Word(SourceInfo.NONE, "j")),
1338 new IntegerLiteral(SourceInfo.NONE, 5));
1339 Statement ts =
1340 new ExpressionStatement(SourceInfo.NONE,
1341 new SimpleAssignmentExpression(SourceInfo.NONE,
1342 new SimpleNameReference(SourceInfo.NONE,
1343 new Word(SourceInfo.NONE, "i")),
1344 new IntegerLiteral(SourceInfo.NONE, 10)));
1345 IfThenStatement ift = new IfThenStatement(SourceInfo.NONE, te, ts);
1346
1347 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
1348 UninitializedVariableDeclarator uvd =
1349 new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
1350 FormalParameter param =
1351 new FormalParameter(SourceInfo.NONE,
1352 new UninitializedVariableDeclarator(SourceInfo.NONE, intt,
1353 new Word(SourceInfo.NONE, "j")), false);
1354 BracedBody bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {
1355 new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1356
1357 ConcreteMethodDef cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1358 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1359 new ReferenceType[0], bb);
1360
1361 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1362 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1363 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1364 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1365
1366 vd1.setEnclosingData(md1);
1367 vd2.setEnclosingData(md1);
1368 md1.addVar(vd1);
1369 md1.addVar(vd2);
1370
1371 LinkedList<VariableData> vars = new LinkedList<VariableData>();
1372 vars.addLast(vd1);
1373 vars.addLast(vd2);
1374 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars,
1375 new LinkedList<Pair<SymbolData, JExpression>>());
1376 _bbtc._bodyData = md1;
1377 _bbtc._data = md1;
1378
1379
1380
1381 ift.visit(_bbtc);
1382 assertTrue("vd1 should be assigned", vd1.hasValue());
1383 assertFalse("vd2 should not be assigned", vd2.hasValue());
1384
1385
1386 //Here, a variable is assigned before the if, so it should still have a value after the if.
1387
1388 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1389 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
1390 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1391 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1392
1393 vd1.setEnclosingData(md1);
1394 vd2.setEnclosingData(md1);
1395 md1.addVar(vd1);
1396 md1.addVar(vd2);
1397
1398 vars = new LinkedList<VariableData>();
1399 vars.addLast(vd1);
1400 vars.addLast(vd2);
1401 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars,
1402 new LinkedList<Pair<SymbolData, JExpression>>());
1403 _bbtc._bodyData = md1;
1404 _bbtc._data = md1;
1405
1406 ift.visit(_bbtc);
1407 assertTrue("vd1 should be assigned", vd1.hasValue());
1408 assertTrue("vd2 should also be assigned", vd2.hasValue());
1409
1410
1411 //test that if a variable is assigned in a branch of the if, and then returned, it is okay.
1412 te = new LessThanExpression(SourceInfo.NONE,
1413 new SimpleNameReference(SourceInfo.NONE,
1414 new Word(SourceInfo.NONE, "j")),
1415 new IntegerLiteral(SourceInfo.NONE, 5));
1416 Statement assignStatement =
1417 new ExpressionStatement(SourceInfo.NONE,
1418 new SimpleAssignmentExpression(SourceInfo.NONE,
1419 new SimpleNameReference(SourceInfo.NONE,
1420 new Word(SourceInfo.NONE, "i")),
1421 new IntegerLiteral(SourceInfo.NONE, 10)));
1422 Statement returnStatement =
1423 new ValueReturnStatement(SourceInfo.NONE,
1424 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
1425 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement, returnStatement}));
1426 ift = new IfThenStatement(SourceInfo.NONE, te, ts);
1427
1428 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1429
1430 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1431 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1432 new ReferenceType[0], bb);
1433
1434 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1435 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1436 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1437 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1438
1439 vd1.setEnclosingData(md1);
1440 vd2.setEnclosingData(md1);
1441 md1.addVar(vd1);
1442 md1.addVar(vd2);
1443
1444 vars = new LinkedList<VariableData>();
1445 vars.addLast(vd1);
1446 vars.addLast(vd2);
1447 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1448 _bbtc._bodyData = md1;
1449 _bbtc._data = md1;
1450
1451 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1452
1453 ift.visit(_bbtc);
1454 assertTrue("vd1 should be assigned", vd1.hasValue());
1455 assertFalse("vd2 should not be assigned", vd2.hasValue());
1456 assertEquals("There should be no errors", 0, errors.size());
1457 errors = new LinkedList<Pair<String, JExpressionIF>>();
1458
1459 // Test that an assignment in the if-expression throws an error
1460 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1461 new IntegerLiteral(SourceInfo.NONE, 5));
1462 ts = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1463 ift = new IfThenStatement(SourceInfo.NONE, te, ts);
1464 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1465
1466 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1467 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1468 new ReferenceType[0], bb);
1469
1470 vd1 = new VariableData("b", _packageMav, SymbolData.BOOLEAN_TYPE, true, null);
1471 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1472 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1473 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1474
1475 vd1.setEnclosingData(md1);
1476 vd2.setEnclosingData(md1);
1477
1478 md1.addVar(vd1);
1479 md1.addVar(vd2);
1480
1481 vars = new LinkedList<VariableData>();
1482 vars.addLast(vd1);
1483 vars.addLast(vd2);
1484 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1485 _bbtc._bodyData = md1;
1486 _bbtc._data = md1;
1487
1488 te = new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "b")),
1489 new BooleanLiteral(SourceInfo.NONE, true));
1490 ts = new ExpressionStatement(SourceInfo.NONE,
1491 new SimpleAssignmentExpression(SourceInfo.NONE,
1492 new SimpleNameReference(SourceInfo.NONE,
1493 new Word(SourceInfo.NONE, "i")),
1494 new IntegerLiteral(SourceInfo.NONE, 10)));
1495 ift = new IfThenStatement(SourceInfo.NONE, te, ts);
1496
1497 ift.visit(_bbtc);
1498 assertEquals("There should now be one error", 1, errors.size());
1499 assertEquals("Error message should be correct", "You cannot use an assignment expression in the conditional " +
1500 "expression of an if-then statement at any language level. Perhaps you meant to compare two " +
1501 "values with '=='",
1502 errors.get(0).getFirst());
1503
1504
1505 }
1506
1507 public void testForIfThenElseStatement() {
1508
1509 //Test that the proper variable assignment happens.
1510 //here, a variable is only assigned in the then branch, so it should not be set after it returns.
1511 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1512 new IntegerLiteral(SourceInfo.NONE, 5));
1513 Statement ts = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1514
1515 IfThenElseStatement ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new EmptyStatement(SourceInfo.NONE));
1516
1517 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
1518 UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
1519 FormalParameter param = new FormalParameter(SourceInfo.NONE, new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
1520 BracedBody bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1521
1522 ConcreteMethodDef cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1523 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1524 new ReferenceType[0], bb);
1525
1526 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1527 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1528 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1529 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1530
1531 vd1.setEnclosingData(md1);
1532 vd2.setEnclosingData(md1);
1533
1534 md1.addVar(vd1);
1535 md1.addVar(vd2);
1536
1537 LinkedList<VariableData> vars = new LinkedList<VariableData>();
1538 vars.addLast(vd1);
1539 vars.addLast(vd2);
1540 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1541 _bbtc._bodyData = md1;
1542 _bbtc._data = md1;
1543
1544 ift.visit(_bbtc);
1545 assertTrue("vd1 should be assigned", vd1.hasValue());
1546 assertFalse("vd2 should not be assigned", vd2.hasValue());
1547
1548 //test that if a variable is only assigned in the else case that it is not assigned afterwards
1549 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1550 new IntegerLiteral(SourceInfo.NONE, 5));
1551 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1552 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
1553 ift = new IfThenElseStatement(SourceInfo.NONE, te, new EmptyStatement(SourceInfo.NONE), ts);
1554
1555 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1556
1557 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1558 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1559 new ReferenceType[0], bb);
1560
1561 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1562 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1563 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1564 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1565
1566 vd1.setEnclosingData(md1);
1567 vd2.setEnclosingData(md1);
1568
1569 md1.addVar(vd1);
1570 md1.addVar(vd2);
1571
1572 vars = new LinkedList<VariableData>();
1573 vars.addLast(vd1);
1574 vars.addLast(vd2);
1575 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1576 _bbtc._bodyData = md1;
1577 _bbtc._data = md1;
1578 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1579
1580 ift.visit(_bbtc);
1581 assertTrue("vd1 should be assigned", vd1.hasValue());
1582 assertFalse("vd2 should not be assigned", vd2.hasValue());
1583 assertEquals("There should be no errors", 0, errors.size());
1584
1585 //Here, a variable is assigned before the if, so it should still have a value after the if.
1586
1587 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1588 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
1589 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1590 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1591
1592 vd1.setEnclosingData(md1);
1593 vd2.setEnclosingData(md1);
1594
1595 md1.addVar(vd1);
1596 md1.addVar(vd2);
1597
1598 vars = new LinkedList<VariableData>();
1599 vars.addLast(vd1);
1600 vars.addLast(vd2);
1601 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1602 _bbtc._bodyData = md1;
1603 _bbtc._data = md1;
1604 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1605
1606 ift.visit(_bbtc);
1607 assertTrue("vd1 should be assigned", vd1.hasValue());
1608 assertTrue("vd2 should also be assigned", vd2.hasValue());
1609
1610
1611 //test that if a variable is assigned in a branch of the if, and then returned, it is okay.
1612 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1613 new IntegerLiteral(SourceInfo.NONE, 5));
1614 assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1615 Statement returnStatement = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
1616 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement, returnStatement}));
1617 ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new EmptyStatement(SourceInfo.NONE));
1618
1619 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1620
1621 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1622 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1623 new ReferenceType[0], bb);
1624
1625 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1626 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1627 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1628 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1629
1630 vd1.setEnclosingData(md1);
1631 vd2.setEnclosingData(md1);
1632
1633 md1.addVar(vd1);
1634 md1.addVar(vd2);
1635
1636 vars = new LinkedList<VariableData>();
1637 vars.addLast(vd1);
1638 vars.addLast(vd2);
1639 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1640 _bbtc._bodyData = md1;
1641 _bbtc._data = md1;
1642 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1643
1644
1645 ift.visit(_bbtc);
1646 assertTrue("vd1 should be assigned", vd1.hasValue());
1647 assertFalse("vd2 should not be assigned", vd2.hasValue());
1648 assertEquals("There should be no errors", 0, errors.size());
1649 errors = new LinkedList<Pair<String, JExpressionIF>>();
1650
1651
1652 //test that if a variable is assigned in the then case that it cannot be used in the else case.
1653 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1654 new IntegerLiteral(SourceInfo.NONE, 5));
1655 assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1656 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
1657 ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new ExpressionStatement(SourceInfo.NONE, new EqualsExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word (SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 32))));
1658
1659 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1660
1661 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1662 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1663 new ReferenceType[0], bb);
1664
1665 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1666 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1667 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1668 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1669
1670 vd1.setEnclosingData(md1);
1671 vd2.setEnclosingData(md1);
1672
1673 md1.addVar(vd1);
1674 md1.addVar(vd2);
1675
1676 vars = new LinkedList<VariableData>();
1677 vars.addLast(vd1);
1678 vars.addLast(vd2);
1679 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1680 _bbtc._bodyData = md1;
1681 _bbtc._data = md1;
1682 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1683
1684
1685 ift.visit(_bbtc);
1686 assertTrue("vd1 should be assigned", vd1.hasValue());
1687 assertFalse("vd2 should not be assigned", vd2.hasValue());
1688 assertEquals("There should be one error", 1, errors.size());
1689 assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", errors.get(0).getFirst());
1690
1691 //test that if a variable is assigned in both cases that it is assigned afterwards
1692 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1693 new IntegerLiteral(SourceInfo.NONE, 5));
1694 assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1695 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
1696 ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, ts);
1697
1698 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1699
1700 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1701 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1702 new ReferenceType[0], bb);
1703
1704 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1705 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1706 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1707 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1708
1709 vd1.setEnclosingData(md1);
1710 vd2.setEnclosingData(md1);
1711
1712 md1.addVar(vd1);
1713 md1.addVar(vd2);
1714
1715 vars = new LinkedList<VariableData>();
1716 vars.addLast(vd1);
1717 vars.addLast(vd2);
1718 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1719 _bbtc._bodyData = md1;
1720 _bbtc._data = md1;
1721 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1722 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1723
1724
1725 ift.visit(_bbtc);
1726 assertTrue("vd1 should be assigned", vd1.hasValue());
1727 assertTrue("vd2 should be assigned", vd2.hasValue());
1728 assertEquals("There should be one error", 1, errors.size());
1729
1730
1731 //Test that if assignment is used in the conditional expression, an error is thrown
1732 te = new PlusAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 5));
1733 assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1734 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
1735 ift = new IfThenElseStatement(SourceInfo.NONE, te, new EmptyStatement(SourceInfo.NONE), ts);
1736
1737 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1738
1739 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1740 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1741 new ReferenceType[0], bb);
1742
1743 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1744 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
1745 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1746 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1747
1748 vd1.setEnclosingData(md1);
1749 vd2.setEnclosingData(md1);
1750
1751 md1.addVar(vd1);
1752 md1.addVar(vd2);
1753
1754 vars = new LinkedList<VariableData>();
1755 vars.addLast(vd1);
1756 vars.addLast(vd2);
1757 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1758 _bbtc._bodyData = md1;
1759 _bbtc._data = md1;
1760 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1761
1762
1763 ift.visit(_bbtc);
1764 assertEquals("There should now be two errors", 2, errors.size());
1765 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());
1766
1767 //test that if one branch returns a value but the other is a break or continue that SymbolData.NOT_FOUND is returned.
1768 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1769 new IntegerLiteral(SourceInfo.NONE, 5));
1770 returnStatement = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
1771 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {returnStatement}));
1772 BreakStatement bs = new UnlabeledBreakStatement(SourceInfo.NONE);
1773 ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, bs);
1774
1775 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift});
1776
1777 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1778 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1779 new ReferenceType[0], bb);
1780
1781 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1782 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
1783 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1784 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1785
1786 vd1.setEnclosingData(md1);
1787 vd2.setEnclosingData(md1);
1788
1789 md1.addVar(vd1);
1790 md1.addVar(vd2);
1791
1792 vars = new LinkedList<VariableData>();
1793 vars.addLast(vd1);
1794 vars.addLast(vd2);
1795 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1796 _bbtc._bodyData = md1;
1797 _bbtc._data = md1;
1798 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1799
1800 assertEquals("Should return SymbolData.NOT_FOUND", SymbolData.NOT_FOUND.getInstanceData(), ift.visit(_bbtc));
1801
1802 assertEquals("There should still be two errors", 2, errors.size());
1803 }
1804
1805 public void testForForStatement() {
1806 //Test that the proper variable assignment happens.
1807 //here, a variable is only assigned in the for init, so it should not be set after it returns.
1808 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1809 new IntegerLiteral(SourceInfo.NONE, 5));
1810
1811 UnparenthesizedExpressionList sel = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10))});
1812 ForStatement fs = new ForStatement(SourceInfo.NONE, sel, te, new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), new EmptyStatement(SourceInfo.NONE));
1813 // IfThenElseStatement ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new EmptyStatement(SourceInfo.NONE));
1814
1815 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
1816 UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
1817 FormalParameter param = new FormalParameter(SourceInfo.NONE, new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
1818 BracedBody bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), fs});
1819
1820 ConcreteMethodDef cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1821 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1822 new ReferenceType[0], bb);
1823
1824 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1825 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1826 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1827 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1828
1829 vd1.setEnclosingData(md1);
1830 vd2.setEnclosingData(md1);
1831
1832 md1.addVar(vd1);
1833 md1.addVar(vd2);
1834
1835 LinkedList<VariableData> vars = new LinkedList<VariableData>();
1836 vars.addLast(vd1);
1837 vars.addLast(vd2);
1838 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1839 _bbtc._bodyData = md1;
1840 _bbtc._data = md1;
1841
1842 fs.visit(_bbtc);
1843 assertTrue("vd1 should be assigned", vd1.hasValue());
1844 assertFalse("vd2 should not be assigned", vd2.hasValue());
1845 assertEquals("There should be no errors", 0, errors.size());
1846
1847 //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
1848 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
1849 new IntegerLiteral(SourceInfo.NONE, 5));
1850 VariableDeclaration vd = new VariableDeclaration (SourceInfo.NONE, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "int"), new Word(SourceInfo.NONE, "i"), new IntegerLiteral(SourceInfo.NONE, 10))});
1851 UnparenthesizedExpressionList sel2 = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {te});
1852 fs = new ForStatement(SourceInfo.NONE, sel, te, sel2, new ExpressionStatement(SourceInfo.NONE, te));
1853
1854 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {fs});
1855
1856 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1857 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1858 new ReferenceType[0], bb);
1859
1860 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1861 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1862 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1863 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1864
1865 vd1.setEnclosingData(md1);
1866 vd2.setEnclosingData(md1);
1867
1868 md1.addVar(vd1);
1869 md1.addVar(vd2);
1870
1871 vars = new LinkedList<VariableData>();
1872 vars.addLast(vd1);
1873 vars.addLast(vd2);
1874 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1875 _bbtc._bodyData = md1;
1876 _bbtc._data = md1;
1877
1878 fs.visit(_bbtc);
1879 assertTrue("vd1 should be assigned", vd1.hasValue());
1880 assertFalse("vd2 should not be assigned", vd2.hasValue());
1881 assertEquals("There should be no errors", 0, errors.size());
1882
1883 //here, a variable is only assigned in the for init and the for body, so it should not be set after it returns.
1884 Statement ts = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
1885 // te = new Expression(SourceInfo.NONE, new ExpressionPiece[] { new Word(SourceInfo.NONE, "j"),
1886 // new Operator(SourceInfo.NONE, "<"), new IntegerLiteral(SourceInfo.NONE, 5)});
1887 sel = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10))});
1888 fs = new ForStatement(SourceInfo.NONE, sel, new EmptyForCondition(SourceInfo.NONE), new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {ts})));
1889 // IfThenElseStatement ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new EmptyStatement(SourceInfo.NONE));
1890
1891 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), fs});
1892
1893 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1894 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1895 new ReferenceType[0], bb);
1896
1897 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1898 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1899 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1900 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1901
1902 vd1.setEnclosingData(md1);
1903 vd2.setEnclosingData(md1);
1904
1905 md1.addVar(vd1);
1906 md1.addVar(vd2);
1907
1908 vars = new LinkedList<VariableData>();
1909 vars.addLast(vd1);
1910 vars.addLast(vd2);
1911 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1912 _bbtc._bodyData = md1;
1913 _bbtc._data = md1;
1914 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
1915
1916 fs.visit(_bbtc);
1917 assertTrue("vd1 should be assigned", vd1.hasValue());
1918 assertFalse("vd2 should not be assigned", vd2.hasValue());
1919 assertEquals("There should be no errors", 0, errors.size());
1920
1921 //here, a variable is assigned before the for init, so it should still be set after it returns.
1922 // te = new Expression(SourceInfo.NONE, new ExpressionPiece[] { new Word(SourceInfo.NONE, "j"),
1923 // new Operator(SourceInfo.NONE, "<"), new IntegerLiteral(SourceInfo.NONE, 5)});
1924 sel = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10))});
1925 fs = new ForStatement(SourceInfo.NONE, sel, new EmptyForCondition(SourceInfo.NONE), new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), new EmptyStatement(SourceInfo.NONE));
1926 // IfThenElseStatement ift = new IfThenElseStatement(SourceInfo.NONE, te, ts, new EmptyStatement(SourceInfo.NONE));
1927
1928 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), fs});
1929
1930 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1931 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1932 new ReferenceType[0], bb);
1933
1934 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1935 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
1936 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1937 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1938
1939 vd1.setEnclosingData(md1);
1940 vd2.setEnclosingData(md1);
1941
1942 md1.addVar(vd1);
1943 md1.addVar(vd2);
1944
1945 vars = new LinkedList<VariableData>();
1946 vars.addLast(vd1);
1947 vars.addLast(vd2);
1948 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1949 _bbtc._bodyData = md1;
1950 _bbtc._data = md1;
1951
1952 fs.visit(_bbtc);
1953 assertTrue("vd1 should be assigned", vd1.hasValue());
1954 assertTrue("vd2 should be assigned", vd2.hasValue());
1955 assertEquals("Should be 0 errors", 0, errors.size());
1956
1957 // make sure that assignment is not allowed in the conditional of the for statement
1958 te = new PlusAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), new IntegerLiteral(SourceInfo.NONE, 5));
1959 vd = new VariableDeclaration (SourceInfo.NONE, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "int"), new Word(SourceInfo.NONE, "i"), new IntegerLiteral(SourceInfo.NONE, 10))});
1960 sel2 = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {te});
1961 fs = new ForStatement(SourceInfo.NONE, sel, te, sel2, new EmptyStatement(SourceInfo.NONE));
1962
1963 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {fs});
1964
1965 cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
1966 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
1967 new ReferenceType[0], bb);
1968
1969 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
1970 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
1971 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
1972 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
1973
1974 vd1.setEnclosingData(md1);
1975 vd2.setEnclosingData(md1);
1976
1977 md1.addVar(vd1);
1978 md1.addVar(vd2);
1979
1980 vars = new LinkedList<VariableData>();
1981 vars.addLast(vd1);
1982 vars.addLast(vd2);
1983 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
1984 _bbtc._bodyData = md1;
1985 _bbtc._data = md1;
1986
1987 te = new PositivePrefixIncrementExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")));
1988 vd = new VariableDeclaration (SourceInfo.NONE, _publicMav, new VariableDeclarator[] { new InitializedVariableDeclarator(SourceInfo.NONE, new PrimitiveType(SourceInfo.NONE, "int"), new Word(SourceInfo.NONE, "i"), new IntegerLiteral(SourceInfo.NONE, 10))});
1989 sel2 = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {te});
1990 fs = new ForStatement(SourceInfo.NONE, sel, te, sel2, new EmptyStatement(SourceInfo.NONE));
1991
1992 fs.visit(_bbtc);
1993 assertEquals("There should be 1 error", 1, errors.size());
1994 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());
1995
1996
1997 }
1998
1999 public void testForWhileStatement() {
2000 //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
2001 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
2002 new IntegerLiteral(SourceInfo.NONE, 5));
2003
2004
2005 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
2006
2007 Statement ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
2008 WhileStatement ws = new WhileStatement(SourceInfo.NONE, te, ts);
2009
2010 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2011 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
2012
2013 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2014 new VariableData[] {vd1}, new String[0], _sd1, null);
2015
2016 vd1.setEnclosingData(md1);
2017 vd2.setEnclosingData(md1);
2018
2019 md1.addVar(vd1);
2020 md1.addVar(vd2);
2021
2022 LinkedList<VariableData> vars = new LinkedList<VariableData>();
2023 vars.addLast(vd1);
2024 vars.addLast(vd2);
2025 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2026 _bbtc._bodyData = md1;
2027 _bbtc._data = md1;
2028 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2029
2030
2031 ws.visit(_bbtc);
2032 assertTrue("vd1 should be assigned", vd1.hasValue());
2033 assertFalse("vd2 should not be assigned", vd2.hasValue());
2034
2035
2036 //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
2037 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2038 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
2039
2040 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2041 new VariableData[] {vd1}, new String[0], _sd1, null);
2042
2043 vd1.setEnclosingData(md1);
2044 vd2.setEnclosingData(md1);
2045
2046 md1.addVar(vd1);
2047 md1.addVar(vd2);
2048
2049 vars = new LinkedList<VariableData>();
2050 vars.addLast(vd1);
2051 vars.addLast(vd2);
2052 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2053 _bbtc._bodyData = md1;
2054 _bbtc._data = md1;
2055 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2056
2057
2058 ws.visit(_bbtc);
2059 assertTrue("vd1 should be assigned", vd1.hasValue());
2060 assertTrue("vd2 should be assigned", vd2.hasValue());
2061
2062
2063 //Test that assignment is not allowed in the condition expression of the while
2064 te = new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 5));
2065 ws = new WhileStatement(SourceInfo.NONE, te, ts);
2066
2067 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2068 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
2069
2070 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2071 new VariableData[] {vd1}, new String[0], _sd1, null);
2072
2073 vd1.setEnclosingData(md1);
2074 vd2.setEnclosingData(md1);
2075
2076 md1.addVar(vd1);
2077 md1.addVar(vd2);
2078
2079 vars = new LinkedList<VariableData>();
2080 vars.addLast(vd1);
2081 vars.addLast(vd2);
2082 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2083 _bbtc._bodyData = md1;
2084 _bbtc._data = md1;
2085 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2086
2087
2088 ws.visit(_bbtc);
2089 assertEquals("There should be 1 error", 1, errors.size());
2090 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());
2091
2092
2093
2094 }
2095
2096 public void testForWhileStatementOnly() {
2097 //Test that a boolean condition expression results in no error
2098 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
2099 new IntegerLiteral(SourceInfo.NONE, 5));
2100
2101
2102 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
2103
2104 Statement ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
2105 WhileStatement ws = new WhileStatement(SourceInfo.NONE, te, ts);
2106 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2107
2108 assertEquals("Should return null", null, _bbtc.forWhileStatementOnly(ws, SymbolData.BOOLEAN_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()));
2109 assertEquals("There should be no errors", 0, errors.size());
2110
2111 //Test that a non-boolean condition expression throws an error
2112 assertEquals("Should return null", null, _bbtc.forWhileStatementOnly(ws, SymbolData.INT_TYPE.getInstanceData(), SymbolData.DOUBLE_TYPE.getInstanceData()));
2113 assertEquals("There should be 1 error", 1, errors.size());
2114 assertEquals("Error message should be correct", "This while-statement's conditional expression must be a boolean value. Instead, its type is int", errors.get(0).getFirst());
2115
2116 //Test "boolean" as a condition
2117 assertEquals("Should return null", null, _bbtc.forWhileStatementOnly(ws, SymbolData.BOOLEAN_TYPE, SymbolData.DOUBLE_TYPE.getInstanceData()));
2118 assertEquals("There should be 2 error", 2, errors.size());
2119 assertEquals("Error message should be correct", "This while-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", errors.getLast().getFirst());
2120 }
2121
2122 public void testForForStatementOnly() {
2123
2124 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
2125 new IntegerLiteral(SourceInfo.NONE, 5));
2126
2127 UnparenthesizedExpressionList sel = new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10))});
2128 ForStatement fs = new ForStatement(SourceInfo.NONE, sel, new NullLiteral(SourceInfo.NONE), new UnparenthesizedExpressionList(SourceInfo.NONE, new Expression[0]), new EmptyStatement(SourceInfo.NONE));
2129
2130
2131 //Test that a boolean condition results in no error
2132 assertEquals("Should return null", null, _bbtc.forForStatementOnly(fs, SymbolData.INT_TYPE, SymbolData.BOOLEAN_TYPE.getInstanceData(), SymbolData.INT_TYPE, SymbolData.INT_TYPE));
2133 assertEquals("There should be no errors", 0, errors.size());
2134
2135
2136 //Test that a non-boolean condition expression throws an error
2137 assertEquals("Should return null", null, _bbtc.forForStatementOnly(fs, SymbolData.INT_TYPE, SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE, SymbolData.CHAR_TYPE));
2138 assertEquals("Should be 1 error", 1, errors.size());
2139 assertEquals("The error message should be correct", "This for-statement's conditional expression must be a boolean value. Instead, its type is double", errors.get(0).getFirst());
2140
2141 //Test "boolean" as the condition
2142 assertEquals("Should return null", null, _bbtc.forForStatementOnly(fs, SymbolData.INT_TYPE, SymbolData.BOOLEAN_TYPE, SymbolData.INT_TYPE, SymbolData.CHAR_TYPE));
2143 assertEquals("Should be 2 error", 2, errors.size());
2144 assertEquals("The error message should be correct", "This for-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", errors.getLast().getFirst());
2145 }
2146
2147 public void testForDoStatement() {
2148 //Test that a variable without a value before the do statement, that is given a value in the body of the while statement, still doesn't have a value afterwards
2149 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
2150 new IntegerLiteral(SourceInfo.NONE, 5));
2151
2152
2153 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
2154
2155 Statement ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
2156 DoStatement ds = new DoStatement(SourceInfo.NONE, ts, te);
2157
2158 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2159 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
2160
2161 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2162 new VariableData[] {vd1}, new String[0], _sd1, null);
2163
2164 vd1.setEnclosingData(md1);
2165 vd2.setEnclosingData(md1);
2166
2167 md1.addVar(vd1);
2168 md1.addVar(vd2);
2169
2170 LinkedList<VariableData> vars = new LinkedList<VariableData>();
2171 vars.addLast(vd1);
2172 vars.addLast(vd2);
2173 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2174 _bbtc._bodyData = md1;
2175 _bbtc._data = md1;
2176 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2177
2178
2179 ds.visit(_bbtc);
2180 assertTrue("vd1 should be assigned", vd1.hasValue());
2181 assertFalse("vd2 should not be assigned", vd2.hasValue());
2182
2183
2184 //Test that a variable with a value before the do statement, that is given a value in the body of the do statement, still has a value afterwards
2185 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2186 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
2187
2188 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2189 new VariableData[] {vd1}, new String[0], _sd1, null);
2190
2191 vd1.setEnclosingData(md1);
2192 vd2.setEnclosingData(md1);
2193
2194 md1.addVar(vd1);
2195 md1.addVar(vd2);
2196
2197 vars = new LinkedList<VariableData>();
2198 vars.addLast(vd1);
2199 vars.addLast(vd2);
2200 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2201 _bbtc._bodyData = md1;
2202 _bbtc._data = md1;
2203 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2204
2205
2206 ds.visit(_bbtc);
2207 assertTrue("vd1 should be assigned", vd1.hasValue());
2208 assertTrue("vd2 should be assigned", vd2.hasValue());
2209
2210
2211 //Test that assignment is not allowed in the condition expression of the do
2212 te = new PlusAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 5));
2213 ds = new DoStatement(SourceInfo.NONE, ts, te);
2214
2215 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
2216 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, true, null);
2217
2218 md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2219 new VariableData[] {vd1}, new String[0], _sd1, null);
2220
2221 vd1.setEnclosingData(md1);
2222 vd2.setEnclosingData(md1);
2223
2224 md1.addVar(vd1);
2225 md1.addVar(vd2);
2226
2227 vars = new LinkedList<VariableData>();
2228 vars.addLast(vd1);
2229 vars.addLast(vd2);
2230 _bbtc = new BodyTypeChecker(md1, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, vars, new LinkedList<Pair<SymbolData, JExpression>>());
2231 _bbtc._bodyData = md1;
2232 _bbtc._data = md1;
2233 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2234
2235
2236 ds.visit(_bbtc);
2237 assertEquals("There should be 1 error", 1, errors.size());
2238 assertEquals("The error message should be correct", "You cannot use an assignment expression in the condition expression of a do statement at any language level", errors.get(0).getFirst());
2239
2240 }
2241
2242 public void testForDoStatementOnly() {
2243 Expression te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
2244 new IntegerLiteral(SourceInfo.NONE, 5));
2245
2246
2247 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
2248
2249 Statement ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement}));
2250 DoStatement ds = new DoStatement(SourceInfo.NONE, ts, te);
2251 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
2252
2253 //Test that a boolean condition results in no error
2254 assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), _bbtc.forDoStatementOnly(ds, SymbolData.INT_TYPE, SymbolData.BOOLEAN_TYPE.getInstanceData()));
2255 assertEquals("There should be no errors", 0, errors.size());
2256
2257 //test that a non-boolean condition expression throws an error
2258 assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), _bbtc.forDoStatementOnly(ds, SymbolData.INT_TYPE, SymbolData.DOUBLE_TYPE.getInstanceData()));
2259 assertEquals("Should be 1 error", 1, errors.size());
2260 assertEquals("The error message should be correct", "This do-statement's conditional expression must be a boolean value. Instead, its type is double", errors.get(0).getFirst());
2261
2262 //test "bool" as the condition
2263 assertEquals("Should return double", SymbolData.DOUBLE_TYPE.getInstanceData(), _bbtc.forDoStatementOnly(ds, SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.BOOLEAN_TYPE));
2264 assertEquals("Should be 2 errors", 2, errors.size());
2265 assertEquals("The error message should be correct", "This do-statement's conditional expression must be a boolean value. Instead, it is a class or interface name", errors.getLast().getFirst());
2266 }
2267
2268
2269 public void testForSwitchStatementOnly() {
2270 //if we did not see a default block, should return null
2271 SwitchStatement ss = new SwitchStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 1), new SwitchCase[0]);
2272 assertEquals("Should return null--no default block", null, _bbtc.forSwitchStatementOnly(ss,
2273 SymbolData.CHAR_TYPE.getInstanceData(),
2274 new TypeData[] {SymbolData.INT_TYPE},
2275 false));
2276
2277 //if any of the blocks are NOT FOUND, should return null
2278 assertEquals("Should return null--has a not-found block", null, _bbtc.forSwitchStatementOnly(ss,
2279 SymbolData.CHAR_TYPE.getInstanceData(),
2280 new TypeData[] {SymbolData.NOT_FOUND, SymbolData.INT_TYPE},
2281 true));
2282
2283 assertEquals("Should return null--has a not-found block", null, _bbtc.forSwitchStatementOnly(ss,
2284 SymbolData.CHAR_TYPE.getInstanceData(),
2285 new TypeData[] {SymbolData.INT_TYPE, SymbolData.NOT_FOUND},
2286 true));
2287
2288
2289 //if last block does not return, statement does not return
2290 assertEquals("Should return null--last block is null", null, _bbtc.forSwitchStatementOnly(ss,
2291 SymbolData.CHAR_TYPE.getInstanceData(),
2292 new TypeData[] {SymbolData.INT_TYPE, SymbolData.CHAR_TYPE, null},
2293 true));
2294
2295
2296 //if all 3 conditions are false, statement does return
2297 assertEquals("Should NOT return null", SymbolData.INT_TYPE.getInstanceData(), _bbtc.forSwitchStatementOnly(ss,
2298 SymbolData.CHAR_TYPE.getInstanceData(),
2299 new TypeData[] {SymbolData.INT_TYPE, SymbolData.CHAR_TYPE, null, SymbolData.CHAR_TYPE},
2300 true));
2301
2302 }
2303
2304 public void testForSwitchStatement() {
2305 _bbtc._vars.addLast(new VariableData("dan", _publicMav, SymbolData.INT_TYPE, true, _bbtc._bodyData));
2306
2307 //assignment in switch expression should throw error
2308 SwitchStatement ss = new SwitchStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new IntegerLiteral(SourceInfo.NONE, 5)), new SwitchCase[0]);
2309 assertEquals("Should return null", null, ss.visit(_bbtc));
2310 assertEquals("Should be 1 error", 1, errors.size());
2311 assertEquals("Error message should be correct", "You cannot use an assignment expression in the switch expression of a switch statement at any language level. Perhaps you meant to compare two values with '=='", errors.getLast().getFirst());
2312
2313 //non int or char value in switch expression
2314 ss = new SwitchStatement(SourceInfo.NONE, new DoubleLiteral(SourceInfo.NONE, 4.2), new SwitchCase[0]);
2315 assertEquals("Should return null", null, ss.visit(_bbtc));
2316 assertEquals("Should be 2 error", 2, errors.size());
2317 assertEquals("Error message should be correct", "The switch expression must be either an int or a char. You have used a double", errors.getLast().getFirst());
2318
2319 //two switch cases with the same label
2320 UnbracedBody emptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[0]);
2321
2322 LabeledCase l1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), emptyBody);
2323 LabeledCase l2 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), emptyBody);
2324 LabeledCase l3 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 7), emptyBody);
2325
2326 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {l1, l2, l3});
2327 assertEquals("Should return null", null, ss.visit(_bbtc));
2328 assertEquals("Should be 3 errors", 3, errors.size());
2329 assertEquals("Error message should be correct", "You cannot have two switch cases with the same label 5", errors.getLast().getFirst());
2330
2331 //two default cases
2332 DefaultCase dc1 = new DefaultCase(SourceInfo.NONE, emptyBody);
2333 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {dc1, dc1});
2334 assertEquals("Should return null", null, ss.visit(_bbtc));
2335 assertEquals("Should be 4 errors", 4, errors.size());
2336 assertEquals("Error message should be correct", "A switch statement can only have one default case", errors.getLast().getFirst());
2337
2338 //x is assigned
2339 VariableData xData = new VariableData("x", _publicMav, SymbolData.INT_TYPE, false, _bbtc._bodyData);
2340 _bbtc._vars.addLast(xData);
2341
2342 ExpressionStatement assignX = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "x")), new IntegerLiteral(SourceInfo.NONE, 5)));
2343 UnbracedBody returnBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {assignX, new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
2344 UnbracedBody breakBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {assignX, new UnlabeledBreakStatement(SourceInfo.NONE)});
2345 UnbracedBody breakNoAssignBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new UnlabeledBreakStatement(SourceInfo.NONE)});
2346 UnbracedBody fallThroughBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {assignX});
2347 UnbracedBody fallThroughNoAssignBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[0]);
2348
2349 SwitchCase c1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), returnBody);
2350 SwitchCase c2 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 6), breakBody);
2351 SwitchCase c3 = new DefaultCase(SourceInfo.NONE, breakBody);
2352
2353 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {c1, c2, c3});
2354
2355 assertEquals("Should return null", null, ss.visit(_bbtc));
2356 assertEquals("Should still be 4 errors", 4, errors.size());
2357 assertTrue("x has been assigned", xData.hasValue());
2358
2359 //x is assigned -- the first block falls through
2360 xData.lostValue();
2361
2362 c1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), fallThroughNoAssignBody);
2363 c2 = new DefaultCase(SourceInfo.NONE, breakBody);
2364 c3 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 6), breakBody);
2365 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {c1, c2, c3});
2366
2367 assertEquals("Should return null", null, ss.visit(_bbtc));
2368 assertEquals("Should still be 4 errors", 4, errors.size());
2369 assertTrue("x has been assigned", xData.hasValue());
2370
2371 //x is not assigned -- the second block does not fall through
2372 xData.lostValue();
2373
2374 c1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), fallThroughNoAssignBody);
2375 c2 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 6), breakNoAssignBody);
2376 c3 = new DefaultCase(SourceInfo.NONE, breakBody);
2377 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {c1, c2, c3});
2378
2379 assertEquals("Should return null", null, ss.visit(_bbtc));
2380 assertEquals("Should still be 4 errors", 4, errors.size());
2381 assertFalse("x has not been assigned", xData.hasValue());
2382
2383 //x is not assigned -- there is no default case
2384 xData.lostValue();
2385
2386 c1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), fallThroughNoAssignBody);
2387 c2 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 6), fallThroughNoAssignBody);
2388 c3 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 7), breakBody);
2389 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {c1, c2, c3});
2390
2391 assertEquals("Should return null", null, ss.visit(_bbtc));
2392 assertEquals("Should still be 4 errors", 4, errors.size());
2393 assertFalse("x has not been assigned", xData.hasValue());
2394
2395 //x is assigned -- the last case is always executed--but an error is added, because it falls through.
2396 xData.lostValue();
2397
2398 c1 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), fallThroughNoAssignBody);
2399 c2 = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 6), fallThroughNoAssignBody);
2400 c3 = new DefaultCase(SourceInfo.NONE, fallThroughBody);
2401 ss = new SwitchStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), new SwitchCase[] {c1, c2, c3});
2402
2403 assertEquals("Should return null", null, ss.visit(_bbtc));
2404 assertEquals("Should be 5 errors", 5, errors.size());
2405 assertEquals("The error message should be correct", "You must end a non-empty switch case with a break or return statement at the Advanced level", errors.getLast().getFirst());
2406 assertTrue("x has been assigned", xData.hasValue());
2407
2408 }
2409
2410 public void testForLabeledCase() {
2411 symbolTable.put("java.lang.String", new SymbolData("java.lang.String"));
2412 UnbracedBody emptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[0]);
2413 //Test a label that is okay
2414 LabeledCase lc = new LabeledCase(SourceInfo.NONE, new CharLiteral(SourceInfo.NONE, 'e'), emptyBody);
2415 assertEquals("Should return null", null, lc.visit(_bbtc));
2416 assertEquals("There should be no errors", 0, errors.size());
2417
2418 lc = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 27), emptyBody);
2419 assertEquals("Should return null", null, lc.visit(_bbtc));
2420 assertEquals("There should be no errors", 0, errors.size());
2421
2422 //Test that a braced body that returns something is handled correctly
2423 UnbracedBody nonEmptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
2424 lc = new LabeledCase(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 27), nonEmptyBody);
2425 TypeData result = lc.visit(_bbtc);
2426 assertEquals("There should be no errors", 0, errors.size());
2427 assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), result);
2428 assertEquals("There should be no errors", 0, errors.size());
2429
2430
2431 //Test some that are not:
2432
2433 //label that is a more complex expression: length greater than 1
2434 lc = new LabeledCase(SourceInfo.NONE, new PlusExpression(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5), new IntegerLiteral(SourceInfo.NONE, 42)), emptyBody);
2435 assertEquals("Should return null", null, lc.visit(_bbtc));
2436 assertEquals("There should be 1 error", 1, errors.size());
2437 assertEquals("The error message should be correct", "The labels of a switch statement must be constants. You are using a more complicated expression of type int", errors.getLast().getFirst());
2438
2439 //label that is a more complex expression: something other than a literal of length 1
2440 _bbtc._vars.addLast(new VariableData("dan", _publicMav, SymbolData.INT_TYPE, true, _bbtc._bodyData));
2441 lc = new LabeledCase(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "dan")), emptyBody);
2442 assertEquals("Should return null", null, lc.visit(_bbtc));
2443 assertEquals("There should now be 2 errors", 2, errors.size());
2444 assertEquals("The error message should be correct", "The labels of a switch statement must be constants. You are using a more complicated expression of type int", errors.getLast().getFirst());
2445
2446 //and a literal whose type is not int or char
2447 lc = new LabeledCase(SourceInfo.NONE, new StringLiteral(SourceInfo.NONE, "hi!"), emptyBody);
2448 assertEquals("Should return null", null, lc.visit(_bbtc));
2449 assertEquals("There should now be 3 errors", 3, errors.size());
2450 assertEquals("The error message should be correct", "The labels of a switch statement must be constants of int or char type. You specified a constant of type java.lang.String", errors.getLast().getFirst());
2451
2452 }
2453
2454 public void testForDefaultCase() {
2455 UnbracedBody emptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[0]);
2456 UnbracedBody returnBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
2457 UnbracedBody breakBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new UnlabeledBreakStatement(SourceInfo.NONE)});
2458
2459 //an empty body
2460 DefaultCase dc = new DefaultCase(SourceInfo.NONE, emptyBody);
2461 assertEquals("Should return null", null, dc.visit(_bbtc));
2462 assertEquals("There should be no errors", 0, errors.size());
2463
2464 //a body with a return statement
2465 dc = new DefaultCase(SourceInfo.NONE, returnBody);
2466 assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), dc.visit(_bbtc));
2467 assertEquals("There should be no errors", 0, errors.size());
2468
2469 //a body with a break
2470 dc = new DefaultCase(SourceInfo.NONE, breakBody);
2471 assertEquals("Should return NOT_FOUND", SymbolData.NOT_FOUND, dc.visit(_bbtc));
2472 assertEquals("There should be no errors", 0, errors.size());
2473 }
2474
2475 public void testForSwitchCase() {
2476 UnbracedBody emptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[0]);
2477 UnbracedBody returnBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5))});
2478 UnbracedBody breakBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new UnlabeledBreakStatement(SourceInfo.NONE)});
2479 UnbracedBody nonEmptyBody = new UnbracedBody(SourceInfo.NONE, new BodyItemI[] {new EmptyStatement(SourceInfo.NONE)});
2480
2481 //empty body
2482 DefaultCase dc = new DefaultCase(SourceInfo.NONE, emptyBody);
2483 assertEquals("Should return null", null, _bbtc.forSwitchCase(dc));
2484 assertEquals("There should be no errors", 0, errors.size());
2485
2486 //return body
2487 dc = new DefaultCase(SourceInfo.NONE, returnBody);
2488 assertEquals("Should return int", SymbolData.INT_TYPE.getInstanceData(), _bbtc.forSwitchCase(dc));
2489 assertEquals("There should be no errors", 0, errors.size());
2490
2491 //break body
2492 dc = new DefaultCase(SourceInfo.NONE, breakBody);
2493 assertEquals("Should return NOT_FOUND", SymbolData.NOT_FOUND, _bbtc.forSwitchCase(dc));
2494 assertEquals("There should be no errors", 0, errors.size());
2495
2496 //non-empty body that does not return: fall-through
2497 dc = new DefaultCase(SourceInfo.NONE, nonEmptyBody);
2498 assertEquals("Should return null", null, _bbtc.forSwitchCase(dc));
2499 assertEquals("There should be one error", 1, errors.size());
2500 assertEquals("The error message should be correct", "You must end a non-empty switch case with a break or return statement at the Advanced level", errors.getLast().getFirst());
2501
2502 }
2503
2504 public void testCreateANewInstanceOfMe() {
2505 //make sure that the correct type of visitor is returned
2506 BodyTypeChecker btc = _bbtc.createANewInstanceOfMe(_bbtc._bodyData, _bbtc._file, _bbtc._package, _bbtc._importedFiles, _bbtc._importedPackages, _bbtc._vars, _bbtc._thrown);
2507 assertTrue("Should be an instance of BodyTypeChecker", btc instanceof BodyTypeChecker);
2508 assertFalse("Should not be an instance of ConstructorBodyTypeChecker", btc instanceof ConstructorBodyTypeChecker);
2509
2510 }
2511
2512 public void testCheckDuplicateExceptions() {
2513 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
2514 Block b = new Block(SourceInfo.NONE, emptyBody);
2515
2516 NormalTryCatchStatement ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[0]);
2517 TryCatchFinallyStatement tcfs = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], b);
2518 _bbtc.checkDuplicateExceptions(ntcs);
2519 _bbtc.checkDuplicateExceptions(tcfs);
2520 assertEquals("Should be no errors", 0, errors.size());
2521
2522 UninitializedVariableDeclarator uvd1 =
2523 new UninitializedVariableDeclarator(SourceInfo.NONE,
2524 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Exception", new Type[0]),
2525 new Word(SourceInfo.NONE, "e"));
2526 UninitializedVariableDeclarator uvd2 =
2527 new UninitializedVariableDeclarator(SourceInfo.NONE,
2528 new ClassOrInterfaceType(SourceInfo.NONE, "RuntimeException", new Type[0]),
2529 new Word(SourceInfo.NONE, "e"));
2530 UninitializedVariableDeclarator uvd3 =
2531 new UninitializedVariableDeclarator(SourceInfo.NONE,
2532 new ClassOrInterfaceType(SourceInfo.NONE, "IOException", new Type[0]),
2533 new Word(SourceInfo.NONE, "e"));
2534
2535 FormalParameter fp1 = new FormalParameter(SourceInfo.NONE, uvd1, false);
2536 FormalParameter fp2 = new FormalParameter(SourceInfo.NONE, uvd2, false);
2537 FormalParameter fp3 = new FormalParameter(SourceInfo.NONE, uvd3, false);
2538
2539 LanguageLevelVisitor llv =
2540 new LanguageLevelVisitor(new File(""),
2541 "",
2542 null, // enclosingClassName for top level traversal
2543 new LinkedList<String>(),
2544 new LinkedList<String>(),
2545 new HashSet<String>(),
2546 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2547 new LinkedList<Command>());
2548
2549 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
2550 llv._errorAdded=false;
2551 // LanguageLevelConverter.symbolTable = llv.symbolTable = new Symboltable();
2552 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
2553 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
2554 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
2555 llv._classesInThisFile = new HashSet<String>();
2556
2557 llv._importedFiles.addLast("java.io.IOException");
2558 SymbolData e = llv.getQualifiedSymbolData("java.lang.Exception", SourceInfo.NONE);
2559 SymbolData re = llv.getQualifiedSymbolData("java.lang.RuntimeException", SourceInfo.NONE);
2560 SymbolData ioe = llv.getQualifiedSymbolData("java.io.IOException", SourceInfo.NONE);
2561
2562 assert symbolTable.containsKey("java.lang.Exception");
2563 assert symbolTable.containsKey("java.lang.RuntimeException");
2564 assert symbolTable.containsKey("java.io.IOException");
2565 assert symbolTable.contains(e);
2566 assert symbolTable.contains(re);
2567 assert symbolTable.contains(ioe);
2568
2569 CatchBlock c1 = new CatchBlock(SourceInfo.NONE, fp1, b);
2570 CatchBlock c2 = new CatchBlock(SourceInfo.NONE, fp2, b);
2571 CatchBlock c3 = new CatchBlock(SourceInfo.NONE, fp3, b);
2572 _bbtc._importedFiles.addLast("java.io.IOException");
2573
2574 // Just one exception, no error
2575 ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[] {c1});
2576 _bbtc.checkDuplicateExceptions(ntcs);
2577 assertEquals("Should be no errors", 0, errors.size());
2578
2579 // Second exception is subclass of 1st exception: should throw error
2580 ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[]{c1, c2});
2581 _bbtc.checkDuplicateExceptions(ntcs);
2582 // System.out.println("First error is: " + errors.get(0));
2583 assertEquals("Should be one error", 1, errors.size());
2584 assertEquals("Error message should be correct", "Exception java.lang.RuntimeException has already been caught", errors.get(0).getFirst());
2585
2586 // Two exceptions, unrelated. no error
2587 ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[]{c2, c3});
2588 _bbtc.checkDuplicateExceptions(ntcs);
2589 assertEquals("Should still be one error", 1, errors.size());
2590
2591 // 2nd and 3rd exceptions subclasses of 1st exception: should throw 2 errors, but one is a duplicate
2592 ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[]{c1, c2, c3});
2593 _bbtc.checkDuplicateExceptions(ntcs);
2594
2595 assertEquals("Should be two errors", 2, errors.size());
2596 assertEquals("2nd Error message should be correct", "Exception java.lang.RuntimeException has already been caught", errors.get(0).getFirst());
2597 assertEquals("3rd Error message should be correct", "Exception java.io.IOException has already been caught", errors.get(1).getFirst());
2598
2599 // 1st exception subclass of 2nd exception: should be no error
2600 ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[]{c2, c1});
2601 _bbtc.checkDuplicateExceptions(ntcs);
2602 assertEquals("Should still be two errors", 2, errors.size());
2603 }
2604
2605 public void testTryCatchLeastRestrictiveType() {
2606
2607 InstanceData[] sdArray = new InstanceData[] {SymbolData.BYTE_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData(), SymbolData.SHORT_TYPE.getInstanceData()};
2608 assertEquals("Should return long type", SymbolData.LONG_TYPE.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(SymbolData.LONG_TYPE.getInstanceData(), sdArray, null));
2609 assertEquals("Should return Object", "java.lang.Object", _bbtc.tryCatchLeastRestrictiveType(SymbolData.SHORT_TYPE.getInstanceData(), sdArray, SymbolData.BOOLEAN_TYPE.getInstanceData()).getName());
2610 assertEquals("Should return double type", SymbolData.DOUBLE_TYPE.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(SymbolData.SHORT_TYPE.getInstanceData(), sdArray, SymbolData.DOUBLE_TYPE.getInstanceData()));
2611 assertEquals("Should return null", null, _bbtc.tryCatchLeastRestrictiveType(null, sdArray, null));
2612 assertEquals("Should return int type", SymbolData.INT_TYPE.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(SymbolData.SHORT_TYPE.getInstanceData(), sdArray, null));
2613 assertEquals("Should return long type", SymbolData.LONG_TYPE.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(null, sdArray, SymbolData.LONG_TYPE.getInstanceData()));
2614
2615 sdArray = new InstanceData[] {null, SymbolData.INT_TYPE.getInstanceData()};
2616 assertEquals("Should return null", null, _bbtc.tryCatchLeastRestrictiveType(SymbolData.INT_TYPE.getInstanceData(), sdArray, null));
2617 assertEquals("Should return short", SymbolData.SHORT_TYPE.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(SymbolData.INT_TYPE.getInstanceData(), sdArray, SymbolData.SHORT_TYPE.getInstanceData()));
2618
2619 SymbolData sd = new SymbolData("java.lang.Object");
2620 SymbolData sd2 = new SymbolData("java.lang.String");
2621 // sd.setIsContinuation(false);
2622 // sd2.setIsContinuation(false);
2623 // symbolTable.put("java.lang.Object", sd);
2624 // symbolTable.put("java.lang.String", sd2);
2625 sd2.setSuperClass(sd);
2626
2627 assertEquals("Should return Object", sd.getInstanceData(), _bbtc.tryCatchLeastRestrictiveType(sd2.getInstanceData(), new InstanceData[]{sd.getInstanceData(), sd2.getInstanceData()}, null));
2628 }
2629
2630 public void testHandleMethodInvocation() {
2631 //handleMethodInvocation(MethodData md, JExpression jexpr)
2632 MethodData md = new MethodData("Fun", _publicMav, new TypeParameter[0], _sd1,
2633 new VariableData[0],
2634 new String[0],
2635 _sd1,
2636 null);
2637
2638 MethodData md2 = new MethodData("InTheSun", _publicMav, new TypeParameter[0], _sd1,
2639 new VariableData[0],
2640 new String[] {"java.lang.RuntimeException", "java.io.IOException"},
2641 _sd1,
2642 null);
2643
2644 NullLiteral nl = new NullLiteral(SourceInfo.NONE);
2645
2646 _bbtc._importedFiles.addLast("java.io.IOException");
2647 // TODO: create LL constructor specifically for testing that only takes file name.
2648 LanguageLevelVisitor llv =
2649 new LanguageLevelVisitor(new File(""), "",
2650 null /* enclosingClassName */,
2651 new LinkedList<String>(),
2652 new LinkedList<String>(),
2653 new HashSet<String>(),
2654 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2655 new LinkedList<Command>());
2656
2657 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
2658 llv._errorAdded = false;
2659 // LanguageLevelConverter.symbolTable.clear(); // done in setUp()
2660 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
2661 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
2662 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
2663 llv._classesInThisFile = new HashSet<String>();
2664
2665 llv._importedFiles.addLast("java.io.IOException");
2666
2667
2668 SymbolData re = llv.getSymbolData("java.lang.RuntimeException", SourceInfo.NONE, true);
2669 SymbolData ioe = llv.getSymbolData("java.io.IOException", SourceInfo.NONE, true);
2670
2671 assert symbolTable.containsKey("java.lang.RuntimeException");
2672 assert symbolTable.containsKey("java.io.IOException");
2673 assert symbolTable.contains(re);
2674 assert symbolTable.contains(ioe);
2675 // symbolTable.put("java.lang.RuntimeException", re);
2676 // symbolTable.put("java.io.IOException", ioe);
2677
2678
2679 _bbtc.handleMethodInvocation(md, nl);
2680 assertEquals("There should be no exceptions in _thrown", 0, _bbtc._thrown.size());
2681
2682 _bbtc.handleMethodInvocation(md2, nl);
2683 assertEquals("There should be 2 exceptions in _thrown", 2, _bbtc._thrown.size());
2684 assertEquals("The first exception should be java.lang.RuntimeException", re, _bbtc._thrown.get(0).getFirst());
2685 assertEquals("The second exception should be java.lang.IOException", ioe, _bbtc._thrown.get(1).getFirst());
2686 }
2687
2688 public void testForThrowStatement() {
2689 LanguageLevelVisitor llv =
2690 new LanguageLevelVisitor(new File(""),
2691 "",
2692 null, // enclosingClassName for top level traversal
2693 new LinkedList<String>(),
2694 new LinkedList<String>(),
2695 new HashSet<String>(),
2696 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2697 new LinkedList<Command>());
2698
2699 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
2700 llv._errorAdded=false;
2701 // LanguageLevelConverter.symbolTable = llv.symbolTable = new Symboltable();
2702 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
2703 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
2704 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
2705 llv._classesInThisFile = new HashSet<String>();
2706
2707 SymbolData re = llv.getSymbolData("java.lang.RuntimeException", SourceInfo.NONE, true);
2708 assert symbolTable.containsKey("java.lang.RuntimeException");
2709 assert symbolTable.contains(re);
2710
2711 // symbolTable.put("java.lang.RuntimeException", re);
2712
2713 VariableData vd = new VariableData("myException", _publicMav, re, true, _bbtc._bodyData);
2714 _bbtc._vars.addLast(vd);
2715 Expression e = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "myException"));
2716 ThrowStatement ts = new ThrowStatement(SourceInfo.NONE, e);
2717
2718 assertEquals("Should return EXCEPTION", SymbolData.EXCEPTION.getInstanceData(), ts.visit(_bbtc));
2719
2720 assertEquals("There should be 1 exception in _thrown", 1, _bbtc._thrown.size());
2721 assertEquals("The exception should be java.lang.RuntimeException", re, _bbtc._thrown.get(0).getFirst());
2722
2723 }
2724
2725 public void testMakeSureCaughtStuffWasThrown() {
2726 //TryCatchStatement that, SymbolData[] caught_array, LinkedList<Pair<SymbolData, JExpression>> thrown) {
2727 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
2728 Block b = new Block(SourceInfo.NONE, emptyBody);
2729
2730 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
2731 UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
2732 FormalParameter param = new FormalParameter(SourceInfo.NONE, new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
2733
2734 NormalTryCatchStatement ntcs =
2735 new NormalTryCatchStatement(SourceInfo.NONE, b,
2736 new CatchBlock[] {new CatchBlock(SourceInfo.NONE, param, b)});
2737 SymbolData javaLangThrowable = _bbtc.getSymbolData("java.lang.Throwable", ntcs, false, true);
2738 // new SymbolData("java.lang.Throwable");
2739 _bbtc.symbolTable.put("java.lang.Throwable", javaLangThrowable);
2740 SymbolData exception = new SymbolData("my.crazy.exception");
2741 exception.setSuperClass(javaLangThrowable);
2742 SymbolData exception2 = new SymbolData("A&M.beat.Rice.in.BaseballException");
2743 exception2.setSuperClass(javaLangThrowable);
2744 SymbolData exception3 = new SymbolData("aegilha");
2745 exception3.setSuperClass(javaLangThrowable);
2746 LinkedList<Pair<SymbolData, JExpression>> thrown = new LinkedList<Pair<SymbolData, JExpression>>();
2747
2748
2749 _bbtc.makeSureCaughtStuffWasThrown(ntcs, new SymbolData[0], thrown);
2750 assertEquals("There should be no errors", 0, errors.size());
2751
2752 Pair<SymbolData, JExpression> p = new Pair<SymbolData, JExpression>(exception, ntcs);
2753 thrown.addLast(p);
2754 _bbtc.makeSureCaughtStuffWasThrown(ntcs, new SymbolData[]{exception}, thrown);
2755 assertEquals("There should still be no errors", 0, errors.size());
2756
2757 thrown.remove(p);
2758
2759 _bbtc.makeSureCaughtStuffWasThrown(ntcs, new SymbolData[] {exception2}, thrown);
2760 // System.err.println("thrown = " + thrown);
2761 // System.err.println("errors = " + errors);
2762 assertEquals("There should be one error", 1, errors.size());
2763 assertEquals("The error message should be correct", "The exception A&M.beat.Rice.in.BaseballException is never thrown in the body of the corresponding try block", errors.get(0).getFirst());
2764 }
2765
2766 public void testIsCheckedException() {
2767 SymbolData th = new SymbolData("java.lang.Throwable");
2768 th.setIsContinuation(false);
2769 SymbolData r = new SymbolData("java.lang.Error");
2770 r.setIsContinuation(false);
2771 r.setSuperClass(th);
2772 SymbolData ex = new SymbolData("java.lang.Exception");
2773 ex.setIsContinuation(false);
2774 ex.setSuperClass(th);
2775 SymbolData re = new SymbolData("java.lang.RuntimeException");
2776 re.setIsContinuation(false);
2777 re.setSuperClass(ex);
2778 symbolTable.put("java.lang.Throwable", th);
2779 symbolTable.put("java.lang.RuntimeException", re);
2780 symbolTable.put("java.lang.Error", r);
2781 symbolTable.put("java.lang.Exception", ex);
2782 SymbolData e1 = new SymbolData("exception1");
2783 e1.setSuperClass(ex);
2784 SymbolData e2 = new SymbolData("exception2");
2785 e2.setSuperClass(re);
2786 SymbolData e3 = new SymbolData("exception3");
2787 e3.setSuperClass(r);
2788
2789 NullLiteral nl = new NullLiteral(SourceInfo.NONE);
2790
2791 assertTrue("Does not subclass RuntimeException or Error", _bbtc.isCheckedException(e1, nl));
2792 assertFalse("Subclasses java.lang.RuntimeException", _bbtc.isCheckedException(e2, nl));
2793 assertFalse("Subclasses java.lang.Error", _bbtc.isCheckedException(e3, nl));
2794
2795 }
2796
2797 public void testIsUncheckedException() {
2798 //Check that extending RuntimeException or Error works as expected
2799 SymbolData th = new SymbolData("java.lang.Throwable");
2800 th.setIsContinuation(false);
2801 SymbolData r = new SymbolData("java.lang.Error");
2802 r.setIsContinuation(false);
2803 r.setSuperClass(th);
2804 SymbolData ex = new SymbolData("java.lang.Exception");
2805 ex.setIsContinuation(false);
2806 ex.setSuperClass(th);
2807 SymbolData re = new SymbolData("java.lang.RuntimeException");
2808 re.setIsContinuation(false);
2809 re.setSuperClass(ex);
2810 symbolTable.put("java.lang.Throwable", th);
2811 symbolTable.put("java.lang.RuntimeException", re);
2812 symbolTable.put("java.lang.Error", r);
2813 symbolTable.put("java.lang.Exception", ex);
2814
2815 SymbolData e1 = new SymbolData("exception1");
2816 e1.setIsContinuation(false);
2817 e1.setSuperClass(ex);
2818 symbolTable.put("exception1", e1);
2819 SymbolData e2 = new SymbolData("exception2");
2820 e2.setSuperClass(re);
2821 SymbolData e3 = new SymbolData("exception3");
2822 e3.setSuperClass(r);
2823 SymbolData e4 = new SymbolData("exception4");
2824 e4.setSuperClass(e1);
2825
2826 NullLiteral nl = new NullLiteral(SourceInfo.NONE);
2827
2828 assertTrue("Does not subclass RuntimeException or Error or anything in the method data", _bbtc.isUncaughtCheckedException(e1, nl));
2829 assertFalse("Subclasses java.lang.RuntimeException", _bbtc.isUncaughtCheckedException(e2, nl));
2830 assertFalse("Subclasses java.lang.Error", _bbtc.isUncaughtCheckedException(e3, nl));
2831
2832 //What if you throw something the method data announces that it throws?
2833 _bbtc._bodyData.getMethodData().setThrown(new String[] {"exception1"});
2834 assertFalse("Is in method data", _bbtc.isUncaughtCheckedException(e1, nl));
2835
2836 assertFalse("Superclass is in method data", _bbtc.isUncaughtCheckedException(e4, nl));
2837
2838
2839 }
2840
2841 public void testHandleUncheckedException() {
2842 JExpression j = new SimpleMethodInvocation(SourceInfo.NONE, new Word(SourceInfo.NONE, "myMethod"), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i"))}));
2843
2844 _bbtc.handleUncheckedException(new SymbolData("i.have.a.shoe"), j);
2845 assertEquals("There should be one error", 1, errors.size());
2846 assertEquals("The error message should be correct", "The method myMethod is declared to throw the exception i.have.a.shoe which needs to be caught or declared to be thrown", errors.get(0).getFirst());
2847 Expression e = new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "myException"));
2848 j = new ThrowStatement(SourceInfo.NONE, e);
2849 _bbtc.handleUncheckedException(new SymbolData("you.have.a.pot"), j);
2850 assertEquals("There should be two errors", 2, errors.size());
2851 assertEquals("The error message should be correct", "This statement throws the exception you.have.a.pot which needs to be caught or declared to be thrown", errors.get(1).getFirst());
2852
2853 }
2854
2855 public void testCompareThrownAndCaught() {
2856
2857 JExpression j =
2858 new SimpleMethodInvocation(SourceInfo.NONE,
2859 new Word(SourceInfo.NONE, "myMethod"),
2860 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i"))}));
2861
2862 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
2863 Block b = new Block(SourceInfo.NONE, emptyBody);
2864
2865 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
2866 UninitializedVariableDeclarator uvd =
2867 new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
2868 FormalParameter param =
2869 new FormalParameter(SourceInfo.NONE,
2870 new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
2871
2872 NormalTryCatchStatement ntcs =
2873 new NormalTryCatchStatement(SourceInfo.NONE,
2874 b,
2875 new CatchBlock[] {new CatchBlock(SourceInfo.NONE, param, b)});
2876
2877 SymbolData javaLangThrowable = _bbtc.getSymbolData("java.lang.Throwable", ntcs, false, true);
2878 // System.err.println("**** In symbol table, java.lang.Throwable = " + symbolTable.get("java.lang.Throwable"));
2879 assertEquals("There should be no errors", 0, errors.size());
2880 javaLangThrowable.setPackage("java.lang"); // Don't know why it is not properly set already
2881 _bbtc.symbolTable.put("java.lang.Throwable", javaLangThrowable);
2882
2883 // System.err.println("***Name for java.lang.Throwable = " + javaLangThrowable.getName());
2884 // System.err.println("***notRightPackage for java.lang.Throwable = " + _bbtc.notRightPackage(javaLangThrowable));
2885 // System.err.println("***Package for java.lang.Throwable = " + javaLangThrowable.getPackage());
2886
2887 SymbolData exception = new SymbolData("my.crazy.exception");
2888 exception.setSuperClass(javaLangThrowable);
2889 SymbolData exception2 = new SymbolData("A&M.beat.Rice.in.BaseballException");
2890 exception2.setSuperClass(javaLangThrowable);
2891 SymbolData exception3 = new SymbolData("aegilha");
2892 exception3.setSuperClass(exception2);
2893 SymbolData[] caught_array = new SymbolData[] { exception, exception2 };
2894 LinkedList<Pair<SymbolData, JExpression>> thrown = new LinkedList<Pair<SymbolData, JExpression>>();
2895 thrown.addLast(new Pair<SymbolData, JExpression>(exception, j));
2896 thrown.addLast(new Pair<SymbolData, JExpression>(exception2, ntcs));
2897 thrown.addLast(new Pair<SymbolData, JExpression>(exception3, ntcs));
2898
2899 assertEquals("There should be no errors", 0, errors.size());
2900 _bbtc.compareThrownAndCaught(ntcs, caught_array, thrown);
2901 for (int i = 0; i < errors.size(); i++)
2902 // System.err.println("Error " + i + ":\n" + errors.get(i).getFirst());
2903 assertEquals("There should be no errors", 0, errors.size());
2904
2905 _bbtc.compareThrownAndCaught(ntcs, new SymbolData[] {exception2}, thrown);
2906 assertEquals("There should be one error", 1, errors.size());
2907 assertEquals("The error message should be correct", "The method myMethod is declared to throw the exception my.crazy.exception which needs to be caught or declared to be thrown", errors.get(0).getFirst());
2908
2909 }
2910
2911 public void testForBracedBody() {
2912 LanguageLevelVisitor llv =
2913 new LanguageLevelVisitor(new File(""),
2914 "",
2915 null, // enclosingClassName for top level traversal
2916 new LinkedList<String>(),
2917 new LinkedList<String>(),
2918 new HashSet<String>(),
2919 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
2920 new LinkedList<Command>());
2921 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
2922 llv._errorAdded=false;
2923 // LanguageLevelConverter.symbolTable = llv.symbolTable = new Symboltable();
2924 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
2925 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
2926 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
2927 llv._classesInThisFile = new HashSet<String>();
2928
2929 // preload symbolTable
2930 SymbolData throwable = llv.getQualifiedSymbolData("java.lang.Throwable");
2931 SymbolData exception = llv.getQualifiedSymbolData("java.lang.Exception");
2932 SymbolData string = llv.getQualifiedSymbolData("java.lang.String");
2933 SymbolData eb = llv.getQualifiedSymbolData("java.util.prefs.BackingStoreException");
2934 SymbolData re = llv.getQualifiedSymbolData("java.lang.RuntimeException");
2935
2936 assert symbolTable.contains(throwable);
2937 assert symbolTable.contains(exception);
2938 assert symbolTable.contains(string);
2939
2940 // System.err.println("Interfaces for java.lang.RuntimeException = " + re.getInterfaces());
2941 //Make sure it is okay to have something else other than an uncaught exception in a braced body.
2942 BracedBody plainBody =
2943 new BracedBody(SourceInfo.NONE, new BodyItemI[] {new UnlabeledBreakStatement(SourceInfo.NONE)});
2944 plainBody.visit(_bbtc);
2945 assertEquals("There should be no errors", 0, errors.size());
2946
2947 //Make sure it is okay to throw a Runtime Exception in a braced body, without catching it.
2948 BracedBody runtimeBB = new BracedBody(SourceInfo.NONE,
2949 new BodyItemI[] {
2950 new ThrowStatement(SourceInfo.NONE,
2951 new SimpleNamedClassInstantiation(SourceInfo.NONE,
2952 new ClassOrInterfaceType(SourceInfo.NONE,
2953 "java.lang.RuntimeException",
2954 new Type[0]),
2955 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
2956 runtimeBB.visit(_bbtc);
2957 assertEquals("There should be no errors", 0, errors.size());
2958
2959 //Make sure it is okay to have a uncaught exception in a braced body, if the method is declared to throw it.
2960 BracedBody bb = new BracedBody(SourceInfo.NONE,
2961 new BodyItemI[] {
2962 new ThrowStatement(SourceInfo.NONE,
2963 new SimpleNamedClassInstantiation(SourceInfo.NONE,
2964 new ClassOrInterfaceType(SourceInfo.NONE,
2965 "java.util.prefs.BackingStoreException",
2966 new Type[0]),
2967 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new StringLiteral(SourceInfo.NONE, "wee")})))});
2968
2969 _bbtc._bodyData.getMethodData().setThrown(new String[]{"java.util.prefs.BackingStoreException"});
2970 _bbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
2971
2972 bb.visit(_bbtc);
2973 assertEquals("There should still be no errors", 0, errors.size());
2974
2975 //make sure it is not okay to have a unchecked exception in a braced body if the method is not declared to throw it.
2976 _bbtc._bodyData.getMethodData().setThrown(new String[0]);
2977 _bbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
2978
2979 bb.visit(_bbtc);
2980
2981 assertEquals("There should be one error", 1, errors.size());
2982 assertEquals("The error message should be correct", "This statement throws the exception java.util.prefs.BackingStoreException which needs to be caught or declared to be thrown", errors.get(0).getFirst());
2983
2984 //make sure that it is not okay to invoke a method that throws an exception if the enclosing method is not declared to throw it.
2985 MethodData badMethod = new MethodData("throwsException",
2986 _packageMav,
2987 new TypeParameter[0],
2988 SymbolData.INT_TYPE,
2989 new VariableData[0],
2990 new String[] {"java.util.prefs.BackingStoreException"},
2991 _sd1,
2992 null);
2993 _bbtc._bodyData.getSymbolData().addMethod(badMethod);
2994 _bbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
2995 BracedBody bbMethod =
2996 new BracedBody(SourceInfo.NONE,
2997 new BodyItemI[] {
2998 new ExpressionStatement(SourceInfo.NONE,
2999 new SimpleMethodInvocation(SourceInfo.NONE,
3000 new Word(SourceInfo.NONE, "throwsException"),
3001 new ParenthesizedExpressionList(SourceInfo.NONE,
3002 new Expression[0])))});
3003 bbMethod.visit(_bbtc);
3004 assertEquals("There should be two errors", 2, errors.size());
3005 assertEquals("The error message should be correct",
3006 "The method throwsException is declared to throw the exception java.util.prefs.BackingStoreException" +
3007 " which needs to be caught or declared to be thrown", errors.getLast().getFirst());
3008
3009 //if enclosing method is delared to throw it, should be okay:
3010 _bbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
3011 bbMethod.visit(_bbtc);
3012 assertEquals("There should still be two errors", 2, errors.size());
3013
3014
3015 //make sure that it is not okay to invoke a constructor that throws an exception if the enclosing method is not declared to throw it
3016 _bbtc._bodyData.getMethodData().setThrown(new String[0]);
3017 _sd3.setMav(_publicMav);
3018 _sd3.setIsContinuation(false);
3019 _bbtc.symbolTable.put(_sd3.getName(), _sd3);
3020 MethodData constructor = new MethodData("zebra", _publicMav, new TypeParameter[0], _sd3, new VariableData[0], new String[] {"java.util.prefs.BackingStoreException"}, _sd3, null);
3021 _sd3.addMethod(constructor);
3022 BracedBody bbConstr = new BracedBody(SourceInfo.NONE, new BodyItemI[]{new ExpressionStatement(SourceInfo.NONE, new SimpleNamedClassInstantiation(SourceInfo.NONE, new ClassOrInterfaceType(SourceInfo.NONE, _sd3.getName(), new Type[0]), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
3023 _bbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
3024 bbConstr.visit(_bbtc);
3025 assertEquals("There should be three errors", 3, errors.size());
3026 assertEquals("The error message should be correct", "The constructor for the class zebra is declared to throw the exception java.util.prefs.BackingStoreException which needs to be caught or declared to be thrown.", errors.getLast().getFirst());
3027
3028
3029 //if enclosing method is delared to throw it, should be okay:
3030 _bbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
3031 bbConstr.visit(_bbtc);
3032 assertEquals("There should still be three errors", 3, errors.size());
3033
3034
3035 }
3036
3037 public void testForTryCatchFinallyStatement() {
3038 LanguageLevelVisitor llv =
3039 new LanguageLevelVisitor(new File(""),
3040 "",
3041 null, // enclosingClassName for top level traversal
3042 new LinkedList<String>(),
3043 new LinkedList<String>(),
3044 new HashSet<String>(),
3045 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
3046 new LinkedList<Command>());
3047 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
3048 llv._errorAdded = false;
3049 // LanguageLevelConverter.symbolTable = llv.symbolTable = symbolTable;
3050 // LanguageLevelConverter._newSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
3051 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
3052 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
3053 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
3054 llv._classesInThisFile = new HashSet<String>();
3055
3056 // preload symbolTable
3057 SymbolData throwable = llv.getQualifiedSymbolData("java.lang.Throwable");
3058 SymbolData exception = llv.getQualifiedSymbolData("java.lang.Exception");
3059 SymbolData string = llv.getQualifiedSymbolData("java.lang.String");
3060 SymbolData eb = llv.getSymbolData("java.util.prefs.BackingStoreException", SourceInfo.NONE, true);
3061 SymbolData re = llv.getSymbolData("java.lang.RuntimeException", SourceInfo.NONE, true);
3062
3063 assert symbolTable.contains(throwable);
3064 assert symbolTable.contains(exception);
3065 assert symbolTable.contains(string);
3066
3067 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
3068 BracedBody bb = new BracedBody(SourceInfo.NONE,
3069 new BodyItemI[] {
3070 new ThrowStatement(SourceInfo.NONE,
3071 new SimpleNamedClassInstantiation(SourceInfo.NONE,
3072 new ClassOrInterfaceType(SourceInfo.NONE,
3073 "java.util.prefs.BackingStoreException",
3074 new Type[0]),
3075 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new StringLiteral(SourceInfo.NONE, "arg")})))});
3076
3077 Block b = new Block(SourceInfo.NONE, bb);
3078 Block b2 = new Block(SourceInfo.NONE, emptyBody);
3079
3080 _bbtc._bodyData.getMethodData().setThrown(new String[0]);
3081 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3082 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3083
3084 //Test that an empty finally block behaves as expected
3085 TryCatchFinallyStatement tcfs = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], b2);
3086 tcfs.visit(_bbtc);
3087 assertEquals("Should be 1 error", 1, errors.size());
3088 assertEquals("Error message should be correct",
3089 "This statement throws the exception java.util.prefs.BackingStoreException which needs to be caught"
3090 + " or declared to be thrown",
3091 errors.getLast().getFirst());
3092
3093 //Test that a finally block where only one branch ends abruptly acts as expected
3094 IfThenElseStatement ites1 = new IfThenElseStatement(SourceInfo.NONE,
3095 new BooleanLiteral(SourceInfo.NONE, true),
3096 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
3097 new EmptyStatement(SourceInfo.NONE));
3098
3099 BracedBody bb2 = new BracedBody(SourceInfo.NONE, new BodyItemI[] {ites1});
3100 TryCatchFinallyStatement tcfs2 = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], new Block(SourceInfo.NONE, bb2));
3101 _bbtc._bodyData.removeAllBlocks();
3102 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3103 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3104 _bbtc._bodyData.resetBlockIterator();
3105
3106 TypeData result = tcfs2.visit(_bbtc); // Duplicates previous error
3107 assertEquals("Should return Exception", SymbolData.EXCEPTION.getInstanceData(), result);
3108 assertEquals("Should still be 1 error", 1, errors.size());
3109 assertEquals("Error message should be correct",
3110 "This statement throws the exception java.util.prefs.BackingStoreException which needs to be caught"
3111 + " or declared to be thrown",
3112 errors.get(0).getFirst());
3113
3114 //Test that a finally block where both branches end abruptly acts as expected (break)
3115 IfThenElseStatement ites2 = new IfThenElseStatement(SourceInfo.NONE,
3116 new BooleanLiteral(SourceInfo.NONE, false),
3117 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 4)),
3118 new UnlabeledBreakStatement(SourceInfo.NONE));
3119
3120 BracedBody bb3 = new BracedBody(SourceInfo.NONE, new BodyItemI[] {ites2});
3121 TryCatchFinallyStatement tcfs3 = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], new Block(SourceInfo.NONE, bb3));
3122
3123 _bbtc._bodyData.removeAllBlocks();
3124 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3125 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3126 _bbtc._bodyData.resetBlockIterator();
3127
3128 assertEquals("Should return Exception", SymbolData.EXCEPTION.getInstanceData(), tcfs3.visit(_bbtc));
3129 assertEquals("Should still be 1 error", 1, errors.size());
3130
3131
3132 //Test that a finally block where both branches end abruptly acts as expected (void return)
3133 _bbtc._bodyData.getMethodData().setReturnType(SymbolData.VOID_TYPE);
3134 IfThenElseStatement ites3 = new IfThenElseStatement(SourceInfo.NONE,
3135 new BooleanLiteral(SourceInfo.NONE, true),
3136 new VoidReturnStatement(SourceInfo.NONE),
3137 new VoidReturnStatement(SourceInfo.NONE));
3138
3139 BracedBody bb4 = new BracedBody(SourceInfo.NONE, new BodyItemI[] {ites3});
3140 TryCatchFinallyStatement tcfs4 = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], new Block(SourceInfo.NONE, bb4));
3141 _bbtc._bodyData.removeAllBlocks();
3142 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3143 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3144 _bbtc._bodyData.resetBlockIterator();
3145
3146 result = tcfs4.visit(_bbtc);
3147 assertEquals("Should return SymbolData.VOID_TYPE", SymbolData.VOID_TYPE.getInstanceData(), result);
3148
3149 assertEquals("Should still still be 1 error", 1, errors.size());
3150
3151 _bbtc._bodyData.getMethodData().setReturnType(SymbolData.INT_TYPE);
3152
3153
3154 // try {
3155 // try b
3156 // finally b2 }
3157 // finally b2
3158
3159 //Test that an error is thrown if a try catch statement is nested, an error is thrown but not caught, and finally doesn't return
3160 TryCatchFinallyStatement inner = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], b2);
3161 TryCatchFinallyStatement nested = new TryCatchFinallyStatement(SourceInfo.NONE,
3162 new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner})),
3163 new CatchBlock[0], b2);
3164
3165 BlockData innerBD = new BlockData(_bbtc._bodyData);
3166 innerBD.addBlock(new BlockData(innerBD));
3167 innerBD.addBlock(new BlockData(innerBD));
3168
3169 _bbtc._bodyData.removeAllBlocks();
3170 _bbtc._bodyData.addBlock(innerBD);
3171 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3172
3173 _bbtc._bodyData.resetBlockIterator();
3174
3175 nested.visit(_bbtc); // Duplicates existing error
3176 assertEquals("There should still be 1 errors", 1, errors.size());
3177 assertEquals("Error message should be correct",
3178 "This statement throws the exception java.util.prefs.BackingStoreException which needs to be caught"
3179 + " or declared to be thrown",
3180 errors.get(0).getFirst());
3181
3182 //Test that no error is thrown if the exception is caught
3183 UninitializedVariableDeclarator uvd1 = new UninitializedVariableDeclarator(SourceInfo.NONE, new ClassOrInterfaceType(SourceInfo.NONE, "java.util.prefs.BackingStoreException", new Type[0]), new Word(SourceInfo.NONE, "e"));
3184 FormalParameter fp1 = new FormalParameter(SourceInfo.NONE, uvd1, false);
3185 BlockData catchBD = new BlockData(_bbtc._bodyData);
3186 VariableData fpData = new VariableData("e", null, eb, true, catchBD);
3187 catchBD.addVar(fpData);
3188
3189 CatchBlock cb = new CatchBlock(SourceInfo.NONE, fp1, b2);
3190 TryCatchFinallyStatement nested2 = new TryCatchFinallyStatement(SourceInfo.NONE, new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner})), new CatchBlock[] {cb}, b2);
3191 _bbtc._bodyData.removeAllBlocks();
3192 innerBD.resetBlockIterator();
3193 _bbtc._bodyData.addBlock(innerBD);
3194 _bbtc._bodyData.addBlock(catchBD);
3195 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3196 _bbtc._bodyData.resetBlockIterator();
3197
3198 nested2.visit(_bbtc);
3199 assertEquals("There should still be 1 error", 1, errors.size());
3200
3201 //Test that no error is thrown if it is a runtime exception
3202 BracedBody reb = new BracedBody(SourceInfo.NONE,
3203 new BodyItemI[] {
3204 new ThrowStatement(SourceInfo.NONE,
3205 new SimpleNamedClassInstantiation(SourceInfo.NONE,
3206 new ClassOrInterfaceType(SourceInfo.NONE,
3207 "java.lang.RuntimeException",
3208 new Type[0]),
3209 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
3210
3211 // try {
3212 // try {
3213 // reb
3214 // }
3215 // finally b2
3216 // }
3217 // finally b2
3218 // }
3219
3220 TryCatchFinallyStatement inner3 = new TryCatchFinallyStatement(SourceInfo.NONE,
3221 new Block(SourceInfo.NONE, reb), new CatchBlock[0], b2);
3222 TryCatchFinallyStatement nested3 = new TryCatchFinallyStatement(SourceInfo.NONE,
3223 new Block(SourceInfo.NONE,
3224 new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner3})),
3225 new CatchBlock[0], b2);
3226
3227 _bbtc._bodyData.removeAllBlocks();
3228 innerBD.resetBlockIterator();
3229 _bbtc._bodyData.addBlock(innerBD);
3230 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3231 _bbtc._bodyData.resetBlockIterator();
3232
3233 nested3.visit(_bbtc);
3234 // System.err.println("Last error was " + errors.getLast().getFirst());
3235 assertEquals("There should still be 1 error", 1, errors.size());
3236
3237 //Test that no error is thrown if the method is declared to throw it
3238 _bbtc._bodyData.getMethodData().setThrown(new String[]{"java.util.prefs.BackingStoreException"});
3239 innerBD.resetBlockIterator();
3240 _bbtc._bodyData.resetBlockIterator();
3241 nested.visit(_bbtc);
3242 assertEquals("There should still be 1 error!", 1, errors.size());
3243 }
3244
3245 public void testForNormalTryCatchStatement() {
3246 LanguageLevelVisitor llv =
3247 new LanguageLevelVisitor(new File(""),
3248 "",
3249 null, // enclosingClassName for top level traversal
3250 new LinkedList<String>(),
3251 new LinkedList<String>(),
3252 new HashSet<String>(),
3253 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
3254 new LinkedList<Command>());
3255 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
3256 llv._errorAdded=false;
3257 // LanguageLevelConverter.symbolTable = llv.symbolTable = symbolTable;
3258 // LanguageLevelConverter._newSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
3259 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
3260 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
3261 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
3262 llv._classesInThisFile = new HashSet<String>();
3263
3264 // preload symbolTable
3265 SymbolData throwable = llv.getQualifiedSymbolData("java.lang.Throwable");
3266 SymbolData exception = llv.getQualifiedSymbolData("java.lang.Exception");
3267 SymbolData string = llv.getQualifiedSymbolData("java.lang.String");
3268 SymbolData eb = llv.getQualifiedSymbolData("java.util.prefs.BackingStoreException");
3269 SymbolData re = llv.getQualifiedSymbolData("java.lang.RuntimeException");
3270
3271 assert symbolTable.contains(throwable);
3272 assert symbolTable.contains(exception);
3273 assert symbolTable.contains(string);
3274
3275 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
3276 BracedBody bb = new BracedBody(SourceInfo.NONE,
3277 new BodyItemI[] {
3278 new ThrowStatement(SourceInfo.NONE,
3279 new SimpleNamedClassInstantiation(SourceInfo.NONE,
3280 new ClassOrInterfaceType(SourceInfo.NONE,
3281 "java.util.prefs.BackingStoreException",
3282 new Type[0]),
3283 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[]{new StringLiteral(SourceInfo.NONE, "boo")})))});
3284
3285 Block b = new Block(SourceInfo.NONE, bb);
3286 Block b2 = new Block(SourceInfo.NONE, emptyBody);
3287
3288 _bbtc._bodyData.getMethodData().setThrown(new String[0]);
3289
3290
3291 // Test that an empty finally block behaves as expected
3292 NormalTryCatchStatement tcfs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[0]);
3293 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3294 tcfs.visit(_bbtc);
3295 assertEquals("Should be 1 error", 1, errors.size());
3296 assertEquals("Error message should be correct",
3297 "This statement throws the exception java.util.prefs.BackingStoreException " +
3298 "which needs to be caught or declared to be thrown",
3299 // "You are attempting to throw java.util.prefs.BackingStoreException, which does not implement the "
3300 // + "Throwable interface",
3301 errors.getLast().getFirst());
3302
3303 // Test that an error is thrown if a try catch statement is nested, an error is thrown but not caught, and finally doesn't return
3304 NormalTryCatchStatement inner = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[0]);
3305 NormalTryCatchStatement nested = new NormalTryCatchStatement(SourceInfo.NONE, new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner})), new CatchBlock[0]);
3306
3307 BlockData innerBD = new BlockData(_bbtc._bodyData);
3308 innerBD.addBlock(new BlockData(innerBD));
3309 innerBD.addBlock(new BlockData(innerBD));
3310
3311 _bbtc._bodyData.removeAllBlocks();
3312 _bbtc._bodyData.addBlock(innerBD);
3313 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3314
3315 _bbtc._bodyData.resetBlockIterator();
3316
3317 nested.visit(_bbtc);
3318 assertEquals("There should still be be 1 error", 1, errors.size()); // Generated error is a duplicate!
3319 assertEquals("Error message should be correct",
3320 // "You are attempting to throw java.util.prefs.BackingStoreException, which does not implement the"
3321 // + " Throwable interface",
3322 "This statement throws the exception java.util.prefs.BackingStoreException " +
3323 "which needs to be caught or declared to be thrown",
3324 errors.get(0).getFirst());
3325
3326 // Test that no error is thrown if the exception is caught
3327 UninitializedVariableDeclarator uvd1 = new UninitializedVariableDeclarator(SourceInfo.NONE, new ClassOrInterfaceType(SourceInfo.NONE, "java.util.prefs.BackingStoreException", new Type[0]), new Word(SourceInfo.NONE, "e"));
3328 FormalParameter fp1 = new FormalParameter(SourceInfo.NONE, uvd1, false);
3329 BlockData catchBD = new BlockData(_bbtc._bodyData);
3330 VariableData fpData = new VariableData("e", null, eb, true, catchBD);
3331 catchBD.addVar(fpData);
3332
3333
3334 CatchBlock cb = new CatchBlock(SourceInfo.NONE, fp1, b2);
3335 NormalTryCatchStatement nested2 = new NormalTryCatchStatement(SourceInfo.NONE, new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner})), new CatchBlock[] {cb});
3336
3337 _bbtc._bodyData.removeAllBlocks();
3338 innerBD.resetBlockIterator();
3339 _bbtc._bodyData.addBlock(innerBD);
3340 _bbtc._bodyData.addBlock(catchBD);
3341 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3342 _bbtc._bodyData.resetBlockIterator();
3343
3344 nested2.visit(_bbtc);
3345 assertEquals("There should still be 1 error", 1, errors.size());
3346
3347 // Test that no error is thrown if it is a runtime exception
3348 BracedBody reb = new BracedBody(SourceInfo.NONE,
3349 new BodyItemI[] {
3350 new ThrowStatement(SourceInfo.NONE,
3351 new SimpleNamedClassInstantiation(SourceInfo.NONE,
3352 new ClassOrInterfaceType(SourceInfo.NONE,
3353 "java.lang.RuntimeException",
3354 new Type[0]),
3355 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
3356
3357 NormalTryCatchStatement inner3 = new NormalTryCatchStatement(SourceInfo.NONE, new Block(SourceInfo.NONE, reb), new CatchBlock[0]);
3358 NormalTryCatchStatement nested3 = new NormalTryCatchStatement(SourceInfo.NONE, new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {inner3})), new CatchBlock[0]);
3359
3360 _bbtc._bodyData.removeAllBlocks();
3361 innerBD.resetBlockIterator();
3362 _bbtc._bodyData.addBlock(innerBD);
3363 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3364 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3365 _bbtc._bodyData.resetBlockIterator();
3366
3367
3368 nested3.visit(_bbtc);
3369 // System.err.println("Last error is: " + errors.getLast().getFirst());
3370 assertEquals("There should still be 1 error", 1, errors.size());
3371
3372 // Test that no error is thrown if the method is declared to throw it
3373 _bbtc._bodyData.getMethodData().setThrown(new String[]{"java.util.prefs.BackingStoreException"});
3374 _bbtc._bodyData.removeAllBlocks();
3375 innerBD.resetBlockIterator();
3376 _bbtc._bodyData.addBlock(innerBD);
3377 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3378 _bbtc._bodyData.addBlock(new BlockData(_bbtc._bodyData));
3379 _bbtc._bodyData.resetBlockIterator();
3380
3381 nested.visit(_bbtc);
3382 assertEquals("There should still be 1 error!", 1, errors.size());
3383 }
3384 }
3385 }