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 /** Do the TypeChecking appropriate to the context of a class body. Common to all Language Levels. */
049 public class ClassBodyTypeChecker extends SpecialTypeChecker {
050
051 /**The SymbolData corresponding to this class.*/
052 private SymbolData _symbolData;
053
054 /**True if we encounter a ConstructorDef while visiting the body.*/
055 protected boolean hasConstructor;
056
057 /** Constructor for ClassBodyTypeChecker. Adds all the variables in the symbol data to the list of what can be seen
058 * from this context, since class fields can always be seen.
059 * @param sd The SymbolData of the class we are type checking.
060 * @param file The File corresponding to the source file we are checking.
061 * @param packageName The package of the source file.
062 * @param importedFiles A list of the names of the classes that are specifically imported in the source file
063 * @param importedPackages A list of the names of the packages that are imported in the source file.
064 * @param vars A list of the variable datas that can be seen and have been given a value before this context
065 * @param thrown The exceptions that are thrown
066 */
067 public ClassBodyTypeChecker(SymbolData sd, File file, String packageName, LinkedList<String> importedFiles,
068 LinkedList<String> importedPackages, LinkedList<VariableData> vars,
069 LinkedList<Pair<SymbolData, JExpression>> thrown) {
070 super(sd, file, packageName, importedFiles, importedPackages, vars, thrown);
071 if (sd == null) throw new RuntimeException("SymbolData is null in new ClassBodyTypeChecker operation");
072 _symbolData = sd;
073 hasConstructor = false;
074 assert _vars == vars;
075
076 LinkedList<VariableData> classVars = sd.getVars();
077
078 // for (VariableData vd: classVars) {
079 // if (vd.isFinal() && vd.gotValue()) thingsThatHaveBeenAssigned.addLast(vd);
080 // }
081
082 _vars.addAll(classVars);
083
084 LinkedList<VariableData> superVars = sd.getAllSuperVars();
085 for (VariableData vd: superVars) {
086 if (vd.isFinal() && vd.gotValue()) thingsThatHaveBeenAssigned.addLast(vd);
087 }
088 _vars.addAll(superVars);
089 }
090
091 /** @return the symbol data corresponding to the class we are visiting*/
092 protected Data _getData() { return _symbolData; }
093
094 /** We need to do this so that expressions (which should only occur in variable initializers and
095 * initializer blocks) can know which fields have already been declared
096 */
097 public TypeData forUninitializedVariableDeclaratorOnly(UninitializedVariableDeclarator that,
098 TypeData typeRes,
099 TypeData nameRes) {
100 Word name = that.getName();
101 String text = that.getName().getText();
102 VariableData vd = getFieldOrVariable(text, _symbolData, _symbolData, name);
103 if (vd == null) {
104 throw new RuntimeException("The field " + text + " was not found in " + _symbolData.getName() + '.');
105 }
106 _vars.addLast(vd);
107 return null;
108 }
109
110 /** Make sure the method is not missing a return type.
111 * If expected and actual are both not null, then their relationship (i.e.
112 * is actual a subclass of expected) is checked when the value return statement that returns
113 * actual is processed.
114 */
115 private void _checkReturnType(SymbolData expected, SymbolData actual, ConcreteMethodDef that) {
116 // If the return type is void, the BodyTypeChecker will make sure there are no return statements with expressions.
117 if (expected == SymbolData.VOID_TYPE) {
118 if (actual == null || actual == SymbolData.VOID_TYPE) {
119 // correct, do nothing
120 }
121 }
122 else {
123 if (actual == null) {
124 _addError("This method is missing a return statement.", that);
125 }
126 }
127 }
128
129 /** Finds the corresponding MethodData for this constructor first. Then visits the body, making sure that
130 * all final fields are assigned a value and that no final fields are reassigned.
131 */
132 public TypeData forConstructorDef(ConstructorDef that) {
133 hasConstructor = true;
134 final TypeData mavRes = that.getMav().visit(this);
135 final TypeData[] parameters_result = makeArrayOfRetType(that.getParameters().length);
136 final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
137 for (int i = 0; i < that.getThrows().length; i++) {
138 throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that); //that.getThrows()[i].visit(this);
139 }
140
141 // We need to match the name and params.
142 // First find the correct MethodData.
143 MethodData md = null;
144 FormalParameter[] fParams = that.getParameters();
145 String[] paramTypes = new String[fParams.length];
146 for (int i = 0; i < fParams.length; i++) {
147 paramTypes[i] = fParams[i].getDeclarator().getType().getName();
148 }
149 LinkedList<MethodData> mds = _symbolData.getMethods();
150 Iterator<MethodData> iter = mds.iterator();
151 while (iter.hasNext()) {
152 boolean match = true;
153 MethodData tempMd = iter.next();
154 if (tempMd.getName().equals(LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()))) {
155 // Check the params.
156 VariableData[] vds = tempMd.getParams();
157 if (paramTypes.length == vds.length) {
158 for (int i = 0; i < paramTypes.length; i++) {
159 // The parameters should be in order. Must also check the unqualified form of the VariableData's type.
160 if(!vds[i].getType().getName().equals(paramTypes[i]) &&
161 !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
162 match = false;
163 break;
164 }
165 }
166 if (match) {
167 md = tempMd;
168 break;
169 }
170 }
171 }
172 }
173 if (md == null) { throw
174 new RuntimeException("The constructor " + LanguageLevelVisitor.getUnqualifiedClassName(_symbolData.getName()) +
175 " was not in the class " + _symbolData.getName() + '.');
176 }
177
178 LinkedList<VariableData> ll = new LinkedList<VariableData>();// = cloneVariableDataList(_vars);
179 VariableData[] vds = md.getParams();
180 for (int i = 0; i<vds.length; i++) {
181 ll.addLast(vds[i]);
182 }
183 ll.addAll(cloneVariableDataList(_vars));
184
185 ConstructorBodyTypeChecker btc = new ConstructorBodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll, new LinkedList<Pair<SymbolData, JExpression>>());
186 final TypeData bodyRes = that.getStatements().visit(btc);
187
188 //make sure the constructor assigns a value to any uninitialized fields (i.e. final fields)
189 LinkedList<VariableData> sdVars = _symbolData.getVars();
190 for (int i = 0; i < sdVars.size(); i++) { // Non-finals automatically get values, so only need to check finals
191 if (! sdVars.get(i).hasValue()) {
192 _addError("The final field " + sdVars.get(i).getName() + " has not been initialized. Make sure you give it a value in this constructor", that);
193 return null;
194 }
195 }
196
197 _symbolData.decrementConstructorCount();
198
199
200 if (_symbolData.getConstructorCount() > 0) {
201 unassignVariableDatas(btc.thingsThatHaveBeenAssigned);
202 }
203
204 return forConstructorDefOnly(that, mavRes, parameters_result, throwsRes, bodyRes);
205 }
206
207
208 /*Basically, a no-op*/
209 public TypeData forConstructorDefOnly(ConstructorDef that, TypeData mavRes, TypeData[] parameters_result, TypeData[] throwsRes, TypeData bodyRes) {
210 return forJExpressionOnly(that);
211 }
212
213 /** Visit all of the fields of the ConcreteMethodDef, and resolve everything. Then, find
214 * the corresponding MethodData in the SymbolData's list of methods. It must match both in name and parameter
215 * types. Keep track of what has been assigned before we visit the method body, and then visit the
216 * method body. Then, make sure the return type of the method is okay, and unassign any variables
217 * for which the assignment should not be visible outside the scope of the method.
218 */
219 public TypeData forConcreteMethodDef(ConcreteMethodDef that) {
220 final TypeData mavRes = that.getMav().visit(this);
221 final TypeData[] typeParamsRes = makeArrayOfRetType(that.getTypeParams().length);
222 for (int i = 0; i < that.getTypeParams().length; i++) {
223 typeParamsRes[i] = that.getTypeParams()[i].visit(this);
224 }
225 final SymbolData resRes = getSymbolData(that.getResult().getName(), _symbolData, that);
226 final TypeData nameRes = that.getName().visit(this);
227 final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
228 for (int i = 0; i < that.getThrows().length; i++) {
229 throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
230 }
231 // We need to match the name and params.
232 // First find the correct MethodData.
233 MethodData md = null;
234 FormalParameter[] fParams = that.getParams();
235 String[] paramTypes = new String[fParams.length];
236 for (int i = 0; i < fParams.length; i++) {
237 paramTypes[i] = fParams[i].getDeclarator().getType().getName();
238 }
239 LinkedList<MethodData> mds = _symbolData.getMethods();
240 Iterator<MethodData> iter = mds.iterator();
241 while (iter.hasNext()) {
242 boolean match = false;
243 MethodData tempMd = iter.next();
244 if (tempMd.getName().equals(that.getName().getText())) {
245 match = true;
246
247 // Check the params.
248 VariableData[] vds = tempMd.getParams();
249 if (paramTypes.length == vds.length) {
250 for (int i = 0; i < paramTypes.length; i++) {
251 // The parameters should be in order. Must also check the unqualified form of the VariableData's type.
252 if(!vds[i].getType().getName().equals(paramTypes[i]) &&
253 !LanguageLevelVisitor.getUnqualifiedClassName(vds[i].getType().getName()).equals(paramTypes[i])) {
254 match = false;
255 break;
256 }
257 }
258 if (match) {
259 md = tempMd;
260 break;
261 }
262 }
263 }
264 }
265 if (md == null) {
266 throw new RuntimeException("Internal Program Error: The method " + that.getName().getText() + " was not in the class " + _symbolData.getName() + ". Please report this bug.");
267 }
268
269 LinkedList<VariableData> ll = new LinkedList<VariableData>();
270 VariableData[] vds = md.getParams();
271 for (int i = 0; i < vds.length; i++) {
272 ll.addLast(vds[i]);
273 }
274 ll.addAll(cloneVariableDataList(_vars));
275
276 LinkedList<VariableData> thingsWeAssigned = new LinkedList<VariableData>();
277 for (int i = 0; i<_symbolData.getVars().size(); i++) {
278 VariableData tempVd = _symbolData.getVars().get(i);
279 if (tempVd.gotValue()) { //then this variable did not have a value previously.
280 thingsWeAssigned.addLast(tempVd);
281 }
282 }
283
284 BodyTypeChecker btc = new BodyTypeChecker(md, _file, _package, _importedFiles, _importedPackages, ll,
285 new LinkedList<Pair<SymbolData, JExpression>>());
286
287 TypeData bodyRes = that.getBody().visit(btc); // We assume that this will return an InstanceData -- the return type of the body
288
289 // This checks to see that the method returns the correct type. It throws its own errors.
290 if (bodyRes != null) {bodyRes = bodyRes.getSymbolData();}
291 _checkReturnType(md.getReturnType(), (SymbolData) bodyRes, that);
292 if (md.getReturnType() != null) {
293 // Ensure that this method doesn't override another method with a different return type.
294 SymbolData.checkDifferentReturnTypes(md, _symbolData, LanguageLevelConverter.OPT.javaVersion());
295 }
296
297 // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
298 final TypeData[] paramsRes = makeArrayOfRetType(that.getParams().length);
299 //
300 // // Why oh why is this necessary; these variables aren't even in scope elsewhere; they should be invisible!!!!
301 // for (int i = 0; i < thingsWeAssigned.size(); i++) {
302 // thingsWeAssigned.get(i).lostValue();
303 // }
304 return resRes;
305 }
306
307 /*
308 * Make sure that this method does not override another method with a different return type,
309 * since this is not allowed in java.
310 */
311 public TypeData forAbstractMethodDef(AbstractMethodDef that) {
312 final TypeData mavRes = that.getMav().visit(this);
313 final TypeData[] typeParamsRes = makeArrayOfRetType(that.getTypeParams().length);
314 for (int i = 0; i < that.getTypeParams().length; i++) {
315 typeParamsRes[i] = that.getTypeParams()[i].visit(this);
316 }
317 final SymbolData resRes = getSymbolData(that.getResult().getName(), _symbolData, that);
318 final TypeData nameRes = that.getName().visit(this);
319
320 // This is not used because this call eventually invokes the forUninitializedVariableDeclarator method above.
321 final TypeData[] paramsRes = makeArrayOfRetType(that.getParams().length);
322 for (int i = 0; i<paramsRes.length; i++) {
323 paramsRes[i] = getSymbolData(that.getParams()[i].getDeclarator().getType().getName(), _symbolData, that.getParams()[i]);
324 }
325 final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
326 for (int i = 0; i < that.getThrows().length; i++) {
327 throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);
328 }
329 // Ensure that this method doesn't override another method with a different return type.
330 MethodData md = _symbolData.getMethod(that.getName().getText(), paramsRes);
331 if (md == null) {
332 throw new RuntimeException("Internal Program Error: Could not find the method " + that.getName().getText() + " in class " + _symbolData.getName() +". Please report this bug.");
333 }
334 SymbolData.checkDifferentReturnTypes(md, _symbolData, LanguageLevelConverter.OPT.javaVersion());
335
336 return resRes;
337 }
338
339 /*Try to resolve the type, and then make sure it can be seen from where we are. */
340 public TypeData forTypeOnly(Type that) {
341 Data sd = getSymbolData(that.getName(), _symbolData, that);
342 if (sd != null) {sd = sd.getOuterData();}
343 while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
344 if (!checkAccess(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _symbolData, "class or interface")) {
345 return null;
346 }
347 sd = sd.getOuterData();
348 }
349 return forJExpressionOnly(that);
350 }
351
352 /**
353 * Test the methods that are defined above.
354 */
355 public static class ClassBodyTypeCheckerTest extends TestCase {
356
357 private ClassBodyTypeChecker _cbbtc;
358
359 private SymbolData _sd1;
360 private SymbolData _sd2;
361 private SymbolData _sd3;
362 private SymbolData _sd4;
363 private SymbolData _sd5;
364 private SymbolData _sd6;
365 private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
366 private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
367 private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
368 private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
369 private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
370 private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
371
372
373 public ClassBodyTypeCheckerTest() {
374 this("");
375 }
376 public ClassBodyTypeCheckerTest(String name) {
377 super(name);
378 }
379
380 public void setUp() {
381 _sd1 = new SymbolData("i.like.monkey");
382 _sd2 = new SymbolData("i.like.giraffe");
383 _sd3 = new SymbolData("zebra");
384 _sd4 = new SymbolData("u.like.emu");
385 _sd5 = new SymbolData("");
386 _sd6 = new SymbolData("cebu");
387
388 errors = new LinkedList<Pair<String, JExpressionIF>>();
389 LanguageLevelConverter.symbolTable.clear();
390 LanguageLevelConverter._newSDs.clear();
391 _cbbtc =
392 new ClassBodyTypeChecker(_sd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(),
393 new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
394 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
395 _cbbtc._importedPackages.addFirst("java.lang");
396 }
397
398 public void testForUninitializedVariableDeclaratorOnly() {
399 VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
400 _sd1.addVar(vd1);
401 UninitializedVariableDeclarator uvd =
402 new UninitializedVariableDeclarator(SourceInfo.NONE,
403 new PrimitiveType(SourceInfo.NONE, "int"),
404 new Word(SourceInfo.NONE, "Mojo"));
405 // uvd.visit(_cbbtc);
406 _cbbtc.forUninitializedVariableDeclaratorOnly(uvd, SymbolData.INT_TYPE, null);
407 assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));
408 }
409
410 public void testForInitializedVariableDeclaratorOnly() {
411 VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _cbbtc._data);
412 _sd1.addVar(vd1);
413 InitializedVariableDeclarator ivd = new InitializedVariableDeclarator(SourceInfo.NONE,
414 new PrimitiveType(SourceInfo.NONE, "int"),
415 new Word(SourceInfo.NONE, "Mojo"),
416 new IntegerLiteral(SourceInfo.NONE, 1));
417 ivd.visit(_cbbtc);
418 assertEquals("There should be no errors.", 0, errors.size());
419 assertTrue("_vars should contain Mojo.", _cbbtc._vars.contains(vd1));
420 ivd = new InitializedVariableDeclarator(SourceInfo.NONE,
421 new PrimitiveType(SourceInfo.NONE, "int"),
422 new Word(SourceInfo.NONE, "Santa's Little Helper"),
423 new IntegerLiteral(SourceInfo.NONE, 1));
424 try {
425 ivd.visit(_cbbtc);
426 fail("Should have thrown a RuntimeException because there's no field named Santa's Little Helper.");
427 }
428 catch (RuntimeException re) {
429 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());
430 }
431 }
432
433 public void test_checkReturnType() {
434 /* Check tests that should work correctly first. */
435 _cbbtc._checkReturnType(SymbolData.VOID_TYPE, null, null);
436 _cbbtc._checkReturnType(SymbolData.VOID_TYPE, SymbolData.VOID_TYPE, null);
437 _cbbtc._checkReturnType(SymbolData.INT_TYPE, SymbolData.INT_TYPE, null);
438 _cbbtc._checkReturnType(SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE, null);
439
440 assertEquals("There should be no errors.", 0, errors.size());
441
442 /* Check tests that should each throw an error. */
443 _cbbtc._checkReturnType(SymbolData.INT_TYPE, null, null);
444 assertEquals("There should now be 1 error", 1, errors.size());
445 assertEquals("The error message should be correct", "This method is missing a return statement.", errors.get(0).getFirst());
446
447 }
448
449 public void testForConcreteMethodDef() {
450 FormalParameter[] fps = new FormalParameter[] {
451 new FormalParameter(SourceInfo.NONE,
452 new UninitializedVariableDeclarator(SourceInfo.NONE,
453 new PrimitiveType(SourceInfo.NONE, "double"),
454 new Word (SourceInfo.NONE, "field1")),
455 false),
456 new FormalParameter(SourceInfo.NONE,
457 new UninitializedVariableDeclarator(SourceInfo.NONE,
458 new PrimitiveType(SourceInfo.NONE, "boolean"),
459 new Word (SourceInfo.NONE, "field2")),
460 false)};
461 ConcreteMethodDef cmd = new ConcreteMethodDef(SourceInfo.NONE,
462 _packageMav,
463 new TypeParameter[0],
464 new PrimitiveType(SourceInfo.NONE, "int"),
465 new Word(SourceInfo.NONE, "methodName"),
466 fps,
467 new ReferenceType[0],
468 new BracedBody(SourceInfo.NONE, new BodyItemI[] {
469 new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 5) )}));
470 MethodData md = new MethodData("methodName",
471 _packageMav,
472 new TypeParameter[0],
473 SymbolData.INT_TYPE,
474 new VariableData[] { new VariableData(SymbolData.DOUBLE_TYPE), new VariableData(SymbolData.BOOLEAN_TYPE) },
475 new String[0],
476 _sd1,
477 null); // no SourceInfo
478 _sd1.addMethod(md);
479 cmd.visit(_cbbtc);
480 assertEquals("There should be no errors.", 0, errors.size());
481
482 cmd = new ConcreteMethodDef(SourceInfo.NONE,
483 _packageMav,
484 new TypeParameter[0],
485 new PrimitiveType(SourceInfo.NONE, "int"),
486 new Word(SourceInfo.NONE, "Selma"),
487 fps,
488 new ReferenceType[0],
489 new BracedBody(SourceInfo.NONE, new BodyItemI[] {
490 new ValueReturnStatement(SourceInfo.NONE,
491 new IntegerLiteral(SourceInfo.NONE, 5))}));
492
493 try {
494 cmd.visit(_cbbtc);
495 fail("Should have thrown a RuntimeException because there's no method named Selma.");
496 }
497 catch (RuntimeException re) {
498 assertEquals("The error message should be correct.", "Internal Program Error: The method Selma was not in the class i.like.monkey. Please report this bug.", re.getMessage());
499 }
500
501
502 //Check that an uninitialized variable is caught:
503 PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
504 UninitializedVariableDeclarator uvd = new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
505
506 Statement s = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
507 ConcreteMethodDef cmd0 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], intt,
508 new Word(SourceInfo.NONE, "invalidMethod"), new FormalParameter[0],
509 new ReferenceType[0], new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), s}));
510 VariableData vd = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
511 MethodData md0 = new MethodData("invalidMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
512 new VariableData[0], new String[0], _sd1, cmd0);
513 _sd1.addMethod(md0);
514 vd.setEnclosingData(md0);
515 md0.addVar(vd);
516
517 _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
518 cmd0.visit(_cbbtc);
519 assertEquals("There should be 1 error", 1, errors.size());
520 assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value", errors.get(0).getFirst());
521
522
523
524 //Check that the lexical scope of an if then statement is handled correctly.
525 Expression te = new LessThanExpression(SourceInfo.NONE,
526 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
527 new IntegerLiteral(SourceInfo.NONE, 5));
528 Statement ts = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
529 IfThenStatement ift = new IfThenStatement(SourceInfo.NONE, te, ts);
530
531
532 FormalParameter param = new FormalParameter(SourceInfo.NONE, new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), false);
533 BracedBody bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}), ift,
534 new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")))});
535
536 ConcreteMethodDef cmd1 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
537 intt, new Word(SourceInfo.NONE, "myMethod"), new FormalParameter[] {param},
538 new ReferenceType[0], bb);
539
540 VariableData vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
541 VariableData vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
542 MethodData md1 = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
543 new VariableData[] {vd1}, new String[0], _sd1, cmd1);
544 _sd1.addMethod(md1);
545 vd1.setEnclosingData(md1);
546 vd2.setEnclosingData(md1);
547 md1.addVar(vd1);
548 md1.addVar(vd2);
549
550 _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
551 cmd1.visit(_cbbtc);
552
553 assertEquals("There should still be 1 error", 1, errors.size()); // Generated error is duplicate
554 assertEquals("The error message should be correct", "You cannot use i because it may not have been given a value",
555 errors.get(0).getFirst());
556
557 //Check that a final variable cannot be reassigned to
558 s = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE,
559 new Word(SourceInfo.NONE, "i")));
560 VariableDeclaration i =
561 new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd});
562 ExpressionStatement se =
563 new ExpressionStatement(SourceInfo.NONE,
564 new SimpleAssignmentExpression(SourceInfo.NONE,
565 new SimpleNameReference(SourceInfo.NONE,
566 new Word(SourceInfo.NONE, "i")),
567 new IntegerLiteral(SourceInfo.NONE, 2)));
568 ExpressionStatement se2 =
569 new ExpressionStatement(SourceInfo.NONE,
570 new SimpleAssignmentExpression(SourceInfo.NONE,
571 new SimpleNameReference(SourceInfo.NONE,
572 new Word(SourceInfo.NONE, "i")),
573 new IntegerLiteral(SourceInfo.NONE, 5)));
574
575 BracedBody b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {i, se, se2, s});
576 ConcreteMethodDef cmd2 =
577 new ConcreteMethodDef(SourceInfo.NONE,
578 _publicMav,
579 new TypeParameter[0], intt,
580 new Word(SourceInfo.NONE, "doubleAssignmentMethod"),
581 new FormalParameter[0],
582 new ReferenceType[0], b);
583
584 VariableData vdi = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, null);
585 MethodData md2 = new MethodData("doubleAssignmentMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
586 new VariableData[0], new String[0], _sd1, cmd2);
587 _sd1.addMethod(md2);
588 vdi.setEnclosingData(md2);
589
590 md2.addVar(vdi);
591
592 _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
593 cmd2.visit(_cbbtc);
594 assertEquals("There should now be 2 error2", 2, errors.size());
595 assertEquals("The error message should be correct",
596 "You cannot assign a value to i because it is immutable and has already been given a value",
597 errors.get(1).getFirst());
598
599
600 //test that if a variable is assigned in a branch of the if, and then returned, it is okay.
601 te = new LessThanExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")),
602 new IntegerLiteral(SourceInfo.NONE, 5));
603 Statement assignStatement = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")), new IntegerLiteral(SourceInfo.NONE, 10)));
604 Statement returnStatement = new ValueReturnStatement(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i")));
605 ts = new Block(SourceInfo.NONE, new BracedBody(SourceInfo.NONE, new BodyItemI[] {assignStatement, returnStatement}));
606 ift = new IfThenStatement(SourceInfo.NONE, te, ts);
607
608 bb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new VariableDeclaration(SourceInfo.NONE, _packageMav, new UninitializedVariableDeclarator[]{uvd}),
609 ift,
610 new ValueReturnStatement(SourceInfo.NONE,
611 new IntegerLiteral(SourceInfo.NONE, 5))});
612
613 ConcreteMethodDef cmd4 = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0],
614 intt, new Word(SourceInfo.NONE, "myMethod3"), new FormalParameter[] {param},
615 new ReferenceType[0], bb);
616
617 vd1 = new VariableData("j", _packageMav, SymbolData.INT_TYPE, true, null);
618 vd2 = new VariableData("i", _packageMav, SymbolData.INT_TYPE, false, null);
619 md1 = new MethodData("myMethod3", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
620 new VariableData[] {vd1}, new String[0], _sd1, cmd4);
621 md1.addVar(vd1);
622 md1.addVar(vd2);
623
624 vd1.setEnclosingData(md1);
625 vd2.setEnclosingData(md1);
626
627
628 _sd1.addMethod(md1);
629 _cbbtc = new ClassBodyTypeChecker(_sd1, _cbbtc._file, _cbbtc._package, _cbbtc._importedFiles, _cbbtc._importedPackages, new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
630
631
632 md1.addBlock(new BlockData(md1));
633 cmd4.visit(_cbbtc);
634 assertEquals("There should still be 2 errors", 2, errors.size());
635 }
636
637 public void testCheckDifferentReturnTypes() {
638 SymbolData superSd = new SymbolData("aiya");
639 _sd1.setSuperClass(superSd);
640 MethodData md3 = new MethodData("methodName",
641 _publicMav,
642 new TypeParameter[0],
643 SymbolData.CHAR_TYPE,
644 new VariableData[0],
645 new String[0],
646 null,
647 null);
648 superSd.addMethod(md3);
649 MethodData md4 = new MethodData("methodName",
650 _publicMav,
651 new TypeParameter[0],
652 SymbolData.INT_TYPE,
653 new VariableData[0],
654 new String[0],
655 superSd,
656 null);
657 MethodDef mDef = new ConcreteMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], new PrimitiveType(SourceInfo.NONE, "int"),
658 new Word(SourceInfo.NONE, "methodName"), new FormalParameter[0], new ReferenceType[0],
659 new BracedBody(SourceInfo.NONE, new BodyItemI[] {new ValueReturnStatement(SourceInfo.NONE, new IntegerLiteral(SourceInfo.NONE, 76))}));
660 _sd1.addMethod(md4);
661 _cbbtc._symbolData = _sd1;
662 mDef.visit(_cbbtc);
663 assertEquals("There should be one error.", 1, errors.size());
664 assertEquals("The error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
665 errors.get(0).getFirst());
666 mDef = new AbstractMethodDef(SourceInfo.NONE, _publicMav, new TypeParameter[0], new PrimitiveType(SourceInfo.NONE, "int"),
667 new Word(SourceInfo.NONE, "methodName"), new FormalParameter[0], new ReferenceType[0]);
668
669 mDef.visit(_cbbtc);
670 assertEquals("There should be two errors.", 2, errors.size());
671 assertEquals("The error message should be correct", "methodName() in " + _sd1.getName() + " cannot override methodName() in aiya; attempting to use different return types",
672 errors.get(1).getFirst());
673 }
674
675 public void testForTypeOnly() {
676 Type t = new PrimitiveType(SourceInfo.NONE, "double");
677 t.visit(_cbbtc);
678 assertEquals("There should be no errors", 0, errors.size());
679
680 SymbolData sd = new SymbolData("Adam");
681 sd.setIsContinuation(false);
682 symbolTable.put("Adam", sd);
683 sd.setMav(_publicMav);
684 t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam", new Type[0]);
685 t.visit(_cbbtc);
686 assertEquals("There should still be no errors", 0, errors.size());
687
688 SymbolData innerSd = new SymbolData("Adam$Wulf");
689 innerSd.setIsContinuation(false);
690 sd.addInnerClass(innerSd);
691 innerSd.setOuterData(sd);
692 innerSd.setMav(_publicMav);
693 _cbbtc.symbolTable.put("USaigehgihdsgslghdlighs", innerSd);
694 t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
695 t.visit(_cbbtc);
696 assertEquals("There should still be no errors", 0, errors.size());
697
698 innerSd.setMav(_privateMav);
699 t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
700 t.visit(_cbbtc);
701
702 String tcSD = _cbbtc._symbolData.getName();
703 assertEquals("There should be one error", 1, errors.size());
704 assertEquals("The error message should be correct",
705 "The class or interface Adam.Wulf in Adam.Wulf is private and cannot be accessed from " + tcSD,
706 errors.get(0).getFirst());
707
708 sd.setMav(_privateMav);
709 innerSd.setMav(_publicMav);
710 t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
711 t.visit(_cbbtc);
712 assertEquals("There should be two errors", 2, errors.size());
713 assertEquals("The error message should be correct",
714 "The class or interface Adam in Adam is private and cannot be accessed from " + tcSD,
715 errors.get(1).getFirst());
716 }
717
718 public void testForConstructorDef() {
719 VariableDeclarator[] vds =
720 new VariableDeclarator[] { new UninitializedVariableDeclarator(SourceInfo.NONE,
721 new PrimitiveType(SourceInfo.NONE, "int"),
722 new Word(SourceInfo.NONE, "i"))};
723
724 VariableDeclaration vd = new VariableDeclaration(SourceInfo.NONE, _finalMav, vds);
725 SimpleNameReference snr =
726 new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "i"));
727 ExpressionStatement es =
728 new ExpressionStatement(SourceInfo.NONE,
729 new SimpleAssignmentExpression(SourceInfo.NONE,
730 snr,
731 new IntegerLiteral(SourceInfo.NONE, 1)));
732 BracedBody cbb = new BracedBody(SourceInfo.NONE, new BodyItemI[] { es });
733 ConstructorDef cd = new ConstructorDef(SourceInfo.NONE,
734 new Word(SourceInfo.NONE, "Jimes"),
735 _publicMav,
736 new FormalParameter[0],
737 new ReferenceType[0],
738 cbb);
739 BracedBody b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {vd, cd});
740 ClassDef classDef =
741 new ClassDef(SourceInfo.NONE,
742 _publicMav,
743 new Word(SourceInfo.NONE, "Jimes"),
744 new TypeParameter[0],
745 new ClassOrInterfaceType(SourceInfo.NONE, "java.io.StreamTokenizer", new Type[0]),
746 new ReferenceType[0],
747 b);
748
749 SymbolData jimes = new SymbolData("Jimes");
750 VariableData vData = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, jimes);
751 _cbbtc._file = new File("Jimes.dj0");
752 jimes.setMav(_publicMav);
753 jimes.setIsContinuation(false);
754 jimes.addVar(vData);
755
756 symbolTable.put("Jimes", jimes);
757
758 // SymbolData obj = _cbbtc.getSymbolData("java.lang.Object", new NullLiteral(SourceInfo.NONE), false, true);
759 SymbolData tokenizer = _cbbtc.getSymbolData("java.io.StreamTokenizer", new NullLiteral(SourceInfo.NONE), false, true);
760 jimes.setSuperClass(tokenizer);
761 SymbolData jutc = defineTestCaseClass();
762
763 assert symbolTable.contains(tokenizer);
764 assert symbolTable.contains(jutc);
765
766 MethodData md =
767 new MethodData("Jimes", _publicMav, new TypeParameter[0], jimes, new VariableData[0], new String[0], jimes, cd);
768 MethodData objMd =
769 new MethodData("java.lang.Object", _publicMav, new TypeParameter[0], tokenizer, new VariableData[0],
770 new String[0], tokenizer, cd);
771 jimes.addMethod(md);
772
773 // assumes an explicit super call with no arguments
774 classDef.visit(_cbbtc);
775 assertEquals("There should be one error", 1, errors.size());
776 assertEquals("Error message should be correct",
777 "You must invoke one of java.io.StreamTokenizer's constructors here. You can either explicitly "
778 + "invoke one of its exisitng constructors or add a constructor with signature: StreamTokenizer().",
779 errors.getLast().getFirst());
780
781 // test that a constructor can set the value of a final field
782 tokenizer.addMethod(objMd); //give super class constructor
783 vData.lostValue();
784
785 classDef.visit(_cbbtc);
786 assertEquals("There should still be one error", 1, errors.size());
787
788 // Since we are going to traverse classDef again, we are resetting the error log.
789 errors.clear();
790 // test that if the constructor does not assign a value to the final field, then an error is thrown
791 vData.lostValue();
792 cbb = new BracedBody(SourceInfo.NONE, new BodyItemI[] {});
793 cd = new ConstructorDef(SourceInfo.NONE,
794 new Word(SourceInfo.NONE, "Jimes"),
795 _publicMav,
796 new FormalParameter[0],
797 new ReferenceType[0],
798 cbb);
799 b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {vd, cd});
800 classDef = new ClassDef(SourceInfo.NONE,
801 _publicMav,
802 new Word(SourceInfo.NONE, "Jimes"),
803 new TypeParameter[0],
804 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
805 new ReferenceType[0], b);
806
807 // System.err.println("***** Starting traversal of classDef");
808 classDef.visit(_cbbtc);
809 // System.err.println("Error 3 for line 803 of ClassBodyTypeChecker is: " + errors.get(2).getFirst());
810 // System.err.println("Error 2 for line 803 of ClassBodyTypeChecker is: " + errors.get(1).getFirst());
811 // System.err.println("Error 1 for line 803 of ClassBodyTypeChecker is: " + errors.get(0).getFirst());
812
813 assertEquals("There should be 2 errors now", 2, errors.size());
814
815 assertEquals("The second error message should be correct",
816 "The final field i has not been initialized. Make sure you give it a value in this constructor",
817 errors.getLast().getFirst());
818
819 //test the case of a constructor that makes a call to another constructor
820 vData = new VariableData("j", _finalMav, SymbolData.INT_TYPE, false, jimes);
821 jimes.setVars(new LinkedList<VariableData>());
822 jimes.addVar(vData);
823
824 LinkedList<VariableData> vs = new LinkedList<VariableData>();
825 vs.addLast(vData);
826 _cbbtc = new ClassBodyTypeChecker(jimes, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), vs, new LinkedList<Pair<SymbolData, JExpression>>());
827 ExpressionStatement assign = new ExpressionStatement(SourceInfo.NONE, new SimpleAssignmentExpression(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "j")), new IntegerLiteral(SourceInfo.NONE, 45)));
828 b = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new ExpressionStatement(SourceInfo.NONE, new SimpleThisConstructorInvocation(SourceInfo.NONE, new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]))), assign});
829 cd = new ConstructorDef(SourceInfo.NONE, new Word(SourceInfo.NONE, "name"), _publicMav, new FormalParameter[0], new ReferenceType[0], b);
830 cd.visit(_cbbtc);
831 assertEquals("There should now be 3 errors", 3, errors.size());
832 assertEquals("The error message should be correct","You cannot assign a value to j because it is immutable and has already been given a value" , errors.getLast().getFirst());
833
834
835 }
836
837 }
838 }