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
044 import junit.framework.TestCase;
045
046 /** Do the TypeChecking appropriate to the context of a constructor body. Common to all Language Levels. */
047 public class ConstructorBodyTypeChecker extends BodyTypeChecker {
048
049 /** Constructor for ConstructorBodyTypeChecker.
050 * @param bodyData The bodyData corresponding to the constructor we are visiting
051 * @param file The File corresponding to the source file we are checking.
052 * @param packageName The package of the source file.
053 * @param importedFiles A list of the names of the classes that are specifically imported in the source file
054 * @param importedPackages A list of the names of the packages that are imported in the source file.
055 * @param vars A list of the variable datas that can be seen and have been given a value before this context
056 * @param thrown The exceptions that are thrown
057 */
058 public ConstructorBodyTypeChecker(BodyData bodyData, File file, String packageName, LinkedList<String> importedFiles,
059 LinkedList<String> importedPackages, LinkedList<VariableData> vars,
060 LinkedList<Pair<SymbolData, JExpression>> thrown) {
061 super(bodyData, file, packageName, importedFiles, importedPackages, vars, thrown);
062 }
063
064
065 /** Creates a new instance of this class for visiting inner bodies. */
066 protected BodyTypeChecker
067 createANewInstanceOfMe(BodyData bodyData, File file, String pakage, LinkedList<String> importedFiles,
068 LinkedList<String> importedPackages, LinkedList<VariableData> vars,
069 LinkedList<Pair<SymbolData, JExpression>> thrown) {
070 return new ConstructorBodyTypeChecker(bodyData, file, pakage, importedFiles, importedPackages, vars, thrown);
071 }
072
073
074 /** This is used in the case where a simple this constructor invocation is allowed. i.e. when it is the first
075 * statement of a constructor body.
076 */
077 public TypeData simpleThisConstructorInvocationAllowed(SimpleThisConstructorInvocation that) {
078 //Verify that a constructor of this form is in this--if not, an error will be thrown by this method invocation.
079 //we continue with this method regardless.
080 String name = LanguageLevelVisitor.getUnqualifiedClassName(_data.getSymbolData().getName());
081 InstanceData[] args = getArgTypesForInvocation(that.getArguments());
082 if (args == null) {return null;}
083 MethodData cd = _lookupMethod(name, _data.getSymbolData(), args, that,
084 "No constructor found in class " + _data.getSymbolData().getName() + " with signature: ",
085 true, _data.getSymbolData());
086
087 if (cd==null) {return null;}
088
089 //set all final fields to have a value--they should have gotten caught.
090 LinkedList<VariableData> myFields = _data.getSymbolData().getVars();
091 for (int i = 0; i<myFields.size(); i++) {
092 if (myFields.get(i).hasModifier("final")) {
093 _vars.get(_vars.indexOf(myFields.get(i))).gotValue();
094 thingsThatHaveBeenAssigned.addLast(_vars.get(_vars.indexOf(myFields.get(i))));
095 }
096 }
097
098 // if constructor is declared to throw exceptions, add them to thrown list:
099 String[] thrown = cd.getThrown();
100 for (int i = 0; i < thrown.length; i++) {
101 _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
102 }
103 return null;
104 }
105
106 /** ComplexThisConstructorInvocations are not ever allowed--throw an appropriate error. */
107 public TypeData complexThisConstructorInvocationNotAllowed(ComplexThisConstructorInvocation that) {
108 _addError("Constructor Invocations of this form are never allowed. A constructor invocation can appear here, "
109 + "but it must either be a super constructor invocation or have the form this(...)", that);
110 return null;
111 }
112
113 /** This is used in the case where a simple super constructor invocation is allowed i.e. when it is the first statement
114 * of a constructor body.
115 */
116 public TypeData simpleSuperConstructorInvocationAllowed(SimpleSuperConstructorInvocation that) {
117
118 SymbolData superClass = _data.getSymbolData().getSuperClass();
119
120 if (superClass == null) { //This should never happen.
121 _addError("The class " + _data.getSymbolData().getName() + " does not have a super class", that);
122 return null;
123 }
124
125 //If the super class is an inner class, a constructor of this form can only be used if it is static.
126 if (superClass.getOuterData() != null && !(superClass.hasModifier("static"))) {
127 _addError(superClass.getName() + " is a non-static inner class of " + superClass.getOuterData().getName() + ". Its constructor must be invoked from an instance of its outer class", that);
128 return null;
129 }
130
131
132 //Look in this's super class and try to match the invocation. If no match is found, the method invocation will add an error.
133 String name = LanguageLevelVisitor.getUnqualifiedClassName(superClass.getName());
134 InstanceData[] args = getArgTypesForInvocation(that.getArguments());
135 if (args == null) return null;
136 MethodData cd = _lookupMethod(name, superClass, args, that,
137 "No constructor found in class " + superClass.getName() + " with signature: ",
138 true, superClass);
139
140 if (cd == null) return null;
141
142 // if constructor is declared to throw exceptions, add them to thrown list:
143 String[] thrown = cd.getThrown();
144 for (int i = 0; i < thrown.length; i++) {
145 _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
146 }
147 return null;
148 }
149
150 /** This is used in the case where a complex super constructor invocation is allowed i.e. when it is the first
151 * statement of a constructor body.
152 */
153 public TypeData complexSuperConstructorInvocationAllowed(ComplexSuperConstructorInvocation that) {
154 //resolve the first part of the super invocation using an ETC and super class and try to match the invocation.
155 ExpressionTypeChecker etc = new ExpressionTypeChecker(_data, _file, _package, _importedFiles, _importedPackages,
156 _vars, _thrown);
157 TypeData enclosingResult = that.getEnclosing().visit(etc);
158 thingsThatHaveBeenAssigned.addAll(etc.thingsThatHaveBeenAssigned);
159
160 if (! assertFound(enclosingResult, that)) return null;
161 SymbolData superClass = _data.getSymbolData().getSuperClass();
162
163 //enclosingResult must be the outerclass of the super class of this class.
164 if (superClass == null) {
165 _addError("A qualified super constructor invocation can only be used to invoke the constructor of your super "
166 + "class from the context of its outer class. The class " + _data.getSymbolData().getName() +
167 " does not have a super class, so you cannot do this here",
168 that);
169 return null;
170 }
171 else if (superClass.getOuterData() == null) {
172 _addError("A qualified super constructor invocation can only be used to invoke the constructor of your super "
173 + "class from the context of its outer class. The super class " + superClass.getName()
174 + " does not have an outer class, so you cannot do this here",
175 that);
176 return null;
177 }
178 else if (enclosingResult == null) {
179 _addError("A qualified super constructor invocation can only be used to invoke the constructor of your super "
180 + "class from the context of its outer class.",
181 that);
182 return null;
183 }
184 else if (superClass.getOuterData() != enclosingResult.getSymbolData()) {
185 _addError("A qualified super constructor invocation can only be used to invoke the constructor of your super "
186 + "class from the context of its outer class. The class or interface "
187 + enclosingResult.getSymbolData().getName() + " is not the outer class of the super class "
188 + superClass.getName(),
189 that);
190 return null;
191 }
192 else if (!enclosingResult.isInstanceType()) {
193 _addError("A qualified super constructor invocation can only be made from the context of an instance of the "
194 + "outer class of the super class. You have specified a type name",
195 that);
196 return null;
197 }
198 else if (superClass.hasModifier("static")) {
199 _addError("A qualified super constructor invocation can only be used to invoke the constructor of a non-static "
200 + "super class from the context of its outer class. The super class " + superClass.getName()
201 + " is a static inner class",
202 that);
203 return null;
204 }
205 String name = LanguageLevelVisitor.getUnqualifiedClassName(superClass.getName());
206 InstanceData[] args = getArgTypesForInvocation(that.getArguments());
207 if (args == null) {return null;}
208 MethodData cd = _lookupMethod(name, superClass, args, that,
209 "No constructor found in class " + superClass.getName() + " with signature: ",
210 true, superClass);
211 if (cd == null) {return null;}
212 //if constructor is declared to throw exceptions, add them to thrown list:
213 String[] thrown = cd.getThrown();
214 for (int i = 0; i<thrown.length; i++) {
215 _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
216 }
217 return null;
218 }
219
220 /** Called when the first line of a constructor is not an explicit constructor invocation.
221 * In this case, if the class has a super class (which it should), we assume there is an implicit call
222 * to the super constructor, passing no arguments.
223 */
224 private void implicitSuperConstructor(BracedBody that) {
225 // System.err.println("implicitSuperConstructor called for " + _data.getSymbolData());
226 SymbolData superClass = _data.getSymbolData().getSuperClass();
227
228 if (superClass == null) { //This should never happen, but if it does, no reason to throw an error.
229 return;
230 }
231
232 //If the super class is an inner class, there cannot be an implicit constructor call
233 if (superClass.getOuterData() != null && !(superClass.hasModifier("static"))) {
234 _addError("There is an implicit call to the constructor of " + superClass.getName() + " here, but " +
235 superClass.getName() + " is a non-static inner class of " + superClass.getOuterData().getName() +
236 ". Thus, you must explicitly invoke its constructor from an instance of its outer class",
237 that);
238 return;
239 }
240
241
242 // Look in this's super class and try to match the invocation. If no match is found, the method invocation will
243 // add an error.
244 String name = LanguageLevelVisitor.getUnqualifiedClassName(superClass.getName());
245
246 // There must be a default constructor with no arguments, or an error is thrown.
247 // (Note--if there were no constructors in the super class at all, the default no arguments constructor would exist.
248 // However, a constructor is always generated for a LanguageLevel file, and any class file that relied on the
249 // default constructor would already have it. Therefore, we can assume that all classes have at least one constructor).
250
251 MethodData cd =
252 _lookupMethod(name,
253 superClass,
254 new InstanceData[0],
255 that,
256 "You must invoke one of " + superClass.getName() +
257 "'s constructors here. You can either explicitly invoke one of its exisitng constructors or "
258 + "add a constructor with signature: ",
259 true, superClass);
260
261 if (cd == null) return;
262 // if constructor is declared to throw exceptions, add them to thrown list:
263 // add BracedBody as the JExpression corresponding to the error
264 String[] thrown = cd.getThrown();
265 for (int i = 0; i<thrown.length; i++) {
266 _thrown.addLast(new Pair<SymbolData, JExpression>(getSymbolData(thrown[i], _getData(), that), that));
267 }
268 return;
269 }
270
271 /** Void return statements are allowed in constructor bodies, since according to java, constructors are
272 * void return methods. However, we treat them as if they returned an instance of the class they
273 * are a constructor for, so we will return that symbol data here.
274 */
275 public TypeData forVoidReturnStatementOnly(VoidReturnStatement that) {
276 // Just return the type the constructor would return.
277 return _bodyData.getSymbolData().getInstanceData();
278 }
279
280 /** Throw an error and return null, becuase constructors cannot have value return statements in their bodies. */
281 public TypeData forValueReturnStatementOnly(ValueReturnStatement that, TypeData valueRes) {
282 _addError("You cannot return a value from a class's constructor", that);
283 return _bodyData.getSymbolData().getInstanceData();
284 }
285
286 /** Walk over the statements in the BracedBody, treating the first line specially. A super constructor
287 * invocation can only appear on the first line of a constructor, so see if there is one on the
288 * first line, and if so, call the appropriate method. If there is no super constructor invocation,
289 * then assume there is an implicit one. Then visit the rest of the statements in the body like normal.
290 * Make sure errors are thrown for any uncaught exceptions.
291 */
292 public TypeData forBracedBody(BracedBody that) {
293 // System.err.println("forBracedBody called in " + _data.getSymbolData() + " for " + that);
294 int startIndex = 0;
295 final TypeData[] items_result = makeArrayOfRetType(that.getStatements().length);
296 if (items_result.length > 0) {
297 // The first line of a constructor is treated specially:
298 if (that.getStatements()[0] instanceof ExpressionStatement) {
299 Expression firstExpression = ((ExpressionStatement) that.getStatements()[0]).getExpression();
300 if (firstExpression instanceof SimpleThisConstructorInvocation) {
301 items_result[0] = simpleThisConstructorInvocationAllowed((SimpleThisConstructorInvocation) firstExpression);
302 startIndex ++;
303 }
304
305 else if (firstExpression instanceof ComplexThisConstructorInvocation) {
306 items_result[0] = complexThisConstructorInvocationNotAllowed((ComplexThisConstructorInvocation) firstExpression);
307 startIndex++;
308 }
309 else if (firstExpression instanceof SimpleSuperConstructorInvocation) {
310 items_result[0] = simpleSuperConstructorInvocationAllowed((SimpleSuperConstructorInvocation) firstExpression);
311 startIndex++;
312 }
313 else if (firstExpression instanceof ComplexSuperConstructorInvocation) {
314 items_result[0] = complexSuperConstructorInvocationAllowed((ComplexSuperConstructorInvocation) firstExpression);
315 startIndex++;
316 }
317 }
318 }
319 if (startIndex == 0) implicitSuperConstructor(that);
320
321 int thrownSize = _thrown.size();
322 // System.err.println("_thrown.size() in " + _data + " = " + thrownSize);
323 for (int j = 0; j < thrownSize; j++) {
324 if (isUncaughtCheckedException(_thrown.get(j).getFirst(), that)) {
325 handleUncheckedException(_thrown.get(j).getFirst(), _thrown.get(j).getSecond());
326 }
327 }
328 // TODO: ???? Provision for field initialization?
329 /** The following is supposed to be equivalent to calling SpecialTypeChecker.forBody(that, items_result) */
330 for (int i = startIndex; i < that.getStatements().length; i++) {
331 items_result[i] = that.getStatements()[i].visit(this);
332 // Walk over what has been thrown and throw an error if it contains an unchecked exception
333 for (int j = thrownSize; j < _thrown.size(); j++) {
334 if (isUncaughtCheckedException(_thrown.get(j).getFirst(), that)) {
335 handleUncheckedException(_thrown.get(j).getFirst(), _thrown.get(j).getSecond());
336 }
337 }
338 }
339
340 return forBracedBodyOnly(that, items_result);
341 }
342
343 /** Tests the methods in the above class. */
344 public static class ConstructorBodyTypeCheckerTest extends TestCase {
345
346 private ConstructorBodyTypeChecker _cbtc;
347
348 private MethodData _bd1;
349 // private MethodData _bd2;
350
351 private SymbolData _sd1;
352 private SymbolData _sd2;
353 private SymbolData _sd3;
354 private SymbolData _sd4;
355 private SymbolData _sd5;
356 private SymbolData _sd6;
357 private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
358 private ModifiersAndVisibility _protectedMav =
359 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
360 private ModifiersAndVisibility _privateMav =
361 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
362 private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
363 private ModifiersAndVisibility _abstractMav =
364 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
365 private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
366
367 public ConstructorBodyTypeCheckerTest() { this(""); }
368 public ConstructorBodyTypeCheckerTest(String name) { super(name); }
369
370 public void setUp() {
371 _sd1 = new SymbolData("i.like.monkey");
372 _sd2 = new SymbolData("i.like.giraffe");
373 _sd3 = new SymbolData("zebra");
374 _sd4 = new SymbolData("u.like.emu");
375 _sd5 = new SymbolData("elephant");
376 _sd6 = new SymbolData("cebu");
377
378 VariableData[] vds = new VariableData[] { new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null),
379 new VariableData(SymbolData.BOOLEAN_TYPE) };
380 _bd1 = new MethodData("monkey",
381 _packageMav,
382 new TypeParameter[0],
383 _sd1,
384 vds,
385 new String[0],
386 _sd1,
387 null); // no SourceInfo
388
389 _bd1.getParams()[0].setEnclosingData(_bd1);
390 _bd1.getParams()[1].setEnclosingData(_bd1);
391
392 errors = new LinkedList<Pair<String, JExpressionIF>>();
393 LanguageLevelConverter.symbolTable.clear();
394 LanguageLevelConverter._newSDs.clear();
395 _bd1.addEnclosingData(_sd1);
396 _bd1.addVars(_bd1.getParams());
397 _cbtc = new ConstructorBodyTypeChecker(_bd1,
398 new File(""),
399 "",
400 new LinkedList<String>(),
401 new LinkedList<String>(),
402 new LinkedList<VariableData>(),
403 new LinkedList<Pair<SymbolData, JExpression>>());
404 _cbtc._importedPackages.addFirst("java.lang");
405 }
406
407
408 public void testForVoidReturnStatementOnly() {
409 _cbtc._bodyData = _bd1; // this body data returns _sd1 (yeah, it's a constructor)
410
411 //test one that works
412 BracedBody bb1 = new BracedBody(SourceInfo.NONE,
413 new BodyItemI[] { new VoidReturnStatement(SourceInfo.NONE)});
414
415 TypeData sd = bb1.visit(_cbtc);
416
417 assertEquals("There should be no errors.", 0, errors.size());
418 assertEquals("Should return i.like.monkey type.", _sd1.getInstanceData(), sd);
419
420 }
421
422 public void testforValueReturnStatementOnly() {
423 //value return statement should always throw an error.
424 BodyItemI[] bis =
425 new BodyItemI[] { new ValueReturnStatement(SourceInfo.NONE,
426 new BooleanLiteral(SourceInfo.NONE, true))};
427 BracedBody bb1 = new BracedBody(SourceInfo.NONE, bis);
428 TypeData sd = bb1.visit(_cbtc);
429 assertEquals("There should be one error", 1, errors.size());
430 assertEquals("Should return i.like.monkey type", _sd1.getInstanceData(), sd);
431 assertEquals("Error message should be correct", "You cannot return a value from a class's constructor",
432 errors.get(0).getFirst());
433
434 }
435
436 public void testCreateANewInstanceOfMe() {
437 //make sure that the correct visitor is returned from createANewInstanceOfMe
438 BodyTypeChecker btc =
439 _cbtc.createANewInstanceOfMe(_cbtc._bodyData, _cbtc._file, _cbtc._package, _cbtc._importedFiles,
440 _cbtc._importedPackages, _cbtc._vars, _cbtc._thrown);
441 assertTrue("Should be an instance of ConstructorBodyTypeChecker", btc instanceof ConstructorBodyTypeChecker);
442 }
443
444 public void testForBracedBody() {
445 LanguageLevelVisitor llv =
446 new LanguageLevelVisitor(new File(""),
447 "",
448 null, // enclosingClassName for top level traversal
449 new LinkedList<String>(),
450 new LinkedList<String>(),
451 new HashSet<String>(),
452 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
453 new LinkedList<Command>());
454 llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
455 llv._errorAdded=false;
456 // LanguageLevelConverter.symbolTable = llv.symbolTable = new Symboltable();
457 llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
458 llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
459 // llv._hierarchy = new Hashtable<String, TypeDefBase>();
460 llv._classesInThisFile = new HashSet<String>();
461
462 // TODO: The next line should be unnecessary because the subsequent two lines should force its loading
463 SymbolData throwable = llv.getSymbolData("java.lang.Exception", SourceInfo.NONE, true);
464 SymbolData eb = llv.getSymbolData("java.util.prefs.BackingStoreException", SourceInfo.NONE, true);
465 SymbolData re = llv.getSymbolData("java.lang.RuntimeException", SourceInfo.NONE, true);
466 // LanguageLevelConverter.symbolTable = symbolTable = llv.symbolTable;
467
468 assert symbolTable.contains(eb);
469 assert symbolTable.contains(re);
470 assert symbolTable.containsKey("java.lang.Throwable");
471 assert symbolTable.containsKey("java.lang.Exception");
472
473 _sd3.setIsContinuation(false);
474 _sd3.setMav(_publicMav);
475 _sd1.setSuperClass(_sd3);
476 // Make sure that it is not okay to invoke a super constructor that throws an exception if the enclosing method
477 // is not declared to throw it
478 _cbtc._bodyData.getMethodData().setThrown(new String[0]);
479 _sd3.setMav(_publicMav);
480 _sd3.setIsContinuation(false);
481 _cbtc.symbolTable.put(_sd3.getName(), _sd3);
482 MethodData constructor = new MethodData("zebra",
483 _publicMav,
484 new TypeParameter[0],
485 _sd3,
486 new VariableData[0],
487 new String[] {"java.util.prefs.BackingStoreException"},
488 _sd3,
489 null);
490 _sd3.addMethod(constructor);
491
492 SimpleSuperConstructorInvocation ssci =
493 new SimpleSuperConstructorInvocation(SourceInfo.NONE,
494 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
495 BracedBody supConstr =
496 new BracedBody(SourceInfo.NONE, new BodyItemI[]{new ExpressionStatement(SourceInfo.NONE, ssci)});
497 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
498 supConstr.visit(_cbtc);
499 assertEquals("There should be one error", 1, errors.size());
500 assertEquals("The error message should be correct",
501 "The constructor of this class's super class could throw the exception " +
502 "java.util.prefs.BackingStoreException, so the enclosing constructor needs to be declared to throw it",
503 errors.getLast().getFirst());
504
505
506 //if enclosing method is delared to throw it, should be okay:
507 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
508 _cbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
509 supConstr.visit(_cbtc);
510 assertEquals("There should still be one error", 1, errors.size());
511
512 // //if implicit reference, still give error.
513 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
514 _cbtc._bodyData.getMethodData().setThrown(new String[0]);
515 BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
516 emptyBody.visit(_cbtc);
517 assertEquals("There should be 2 errors", 2, errors.size());
518 assertEquals("The error message should be correct", "There is an implicit call to the superclass's constructor here. That constructor could throw the exception java.util.prefs.BackingStoreException, so the enclosing constructor needs to be declared to throw it", errors.getLast().getFirst());
519
520 //if enclosing method is delared to throw it, should be okay:
521 _cbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
522 emptyBody.visit(_cbtc);
523 assertEquals("There should still be two errors", 2, errors.size());
524
525 //make sure that it is not okay to invoke a this constructor that throws an exception if the enclosing constructor is not declared to throw it
526 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
527 BracedBody thisConstr = new BracedBody(SourceInfo.NONE, new BodyItemI[]{new ExpressionStatement(SourceInfo.NONE, new SimpleThisConstructorInvocation(SourceInfo.NONE, new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
528
529 MethodData thisConstructor = new MethodData("cebu", _publicMav, new TypeParameter[0], _sd6, new VariableData[0], new String[] {"java.util.prefs.BackingStoreException"}, _sd6, null);
530 MethodData thisConstructorNoThrown = new MethodData("cebu", _publicMav, new TypeParameter[0], _sd6, new VariableData[0], new String[0], _sd6, null);
531 _sd6.addMethod(thisConstructor);
532 BodyData oldData = _cbtc._bodyData;
533 _cbtc._data = thisConstructorNoThrown;
534 _cbtc._bodyData = thisConstructorNoThrown;
535
536 thisConstr.visit(_cbtc);
537 assertEquals("There should be 3 errors", 3, errors.size());
538 assertEquals("The error message should be correct", "This constructor could throw the exception java.util.prefs.BackingStoreException, so this enclosing constructor needs to be declared to throw it", errors.getLast().getFirst());
539
540 //if enclosing method is delared to throw it, should be okay:
541 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
542 _cbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
543 thisConstr.visit(_cbtc);
544 assertEquals("There should still be 3 errors", 3, errors.size());
545
546
547 //make sure that it is not okay to invoke a complex super constructor that throws an exception if the enlcosing constructor is not declared to throw it.
548 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
549
550 _sd5.setIsContinuation(false);
551 _sd5.addInnerClass(_sd3);
552 _sd3.setOuterData(_sd5);
553
554 _sd5.setMav(_publicMav);
555 _sd5.setIsContinuation(false);
556 symbolTable.put("elephant", _sd5);
557
558 BracedBody complexSC = new BracedBody(SourceInfo.NONE, new BodyItemI[] {new ExpressionStatement(SourceInfo.NONE, new ComplexSuperConstructorInvocation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "e")), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0])))});
559 oldData.getMethodData().setThrown(new String[0]);
560 _cbtc._vars.add(new VariableData("e", _publicMav, _sd5, true, _sd3));
561 _cbtc._data = oldData;
562 _cbtc._bodyData = oldData;
563
564 complexSC.visit(_cbtc);
565 assertEquals("There should be 4 errors", 4, errors.size());
566 assertEquals("Error message should be correct", "The constructor of this class's super class could throw the exception java.util.prefs.BackingStoreException, so the enclosing constructor needs to be declared to throw it", errors.getLast().getFirst());
567
568 //if enclosing method is delared to throw it, should be okay:
569 _cbtc._thrown = new LinkedList<Pair<SymbolData, JExpression>>();
570
571 _cbtc._bodyData.getMethodData().setThrown(new String[] {"java.util.prefs.BackingStoreException"});
572 complexSC.visit(_cbtc);
573 assertEquals("There should still be 4 errors", 4, errors.size());
574
575 }
576
577 public void testSimpleThisConstructorInvocationAllowed() {
578 // if there is a constructor of the right form, all variable datas will be given a value
579 MethodData constructor =
580 new MethodData("zebra", _publicMav, new TypeParameter[0], _sd3,
581 new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, new String[0], _sd3, null);
582 _sd3.addMethod(constructor);
583 _cbtc._bodyData = constructor;
584 _cbtc._data = constructor;
585
586 VariableData vd1 = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, _sd3);
587 VariableData vd2 = new VariableData("d", _finalMav, SymbolData.DOUBLE_TYPE, false, _sd3);
588 VariableData vd3 = new VariableData("notFinal", _publicMav, SymbolData.BOOLEAN_TYPE, false, _sd3);
589 _cbtc._vars.add(vd1);
590 _cbtc._vars.add(vd2);
591 _cbtc._vars.add(vd3);
592 _sd3.addVar(vd1);
593 _sd3.addVar(vd2);
594 _sd3.addVar(vd3);
595
596 SimpleThisConstructorInvocation constr =
597 new SimpleThisConstructorInvocation(SourceInfo.NONE,
598 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {
599 new IntegerLiteral(SourceInfo.NONE, 5)}));
600 _cbtc.simpleThisConstructorInvocationAllowed(constr);
601 assertEquals("Should be no errors", 0, errors.size());
602 assertEquals("vd1 should have value", true, vd1.hasValue());
603 assertEquals("vd2 should have value", true, vd2.hasValue());
604 assertEquals("vd3 is not final, and thus should not have a value", false, vd3.hasValue());
605 assertEquals("thrown should have 0 elements", 0, _cbtc._thrown.size());
606
607 //if there is not a constructor of the right form, an error will be given
608 vd1.lostValue();
609 vd2.lostValue();
610
611 SimpleThisConstructorInvocation constr2 = new SimpleThisConstructorInvocation(SourceInfo.NONE, new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
612 _cbtc.simpleThisConstructorInvocationAllowed(constr2);
613 assertEquals("Should be one error", 1, errors.size());
614 assertEquals("Error message should be correct", "No constructor found in class zebra with signature: zebra().", errors.getLast().getFirst());
615 assertFalse("vd1 should not have value", vd1.hasValue());
616 assertFalse("vd2 should not have value", vd2.hasValue());
617 assertFalse("vd3 should not have a value", vd3.hasValue());
618 assertEquals("thrown should have 0 elements", 0, _cbtc._thrown.size());
619
620
621 //if there is a constructor of the right form, but it throws an exception, the exception will be added to the _thrown list.
622 constructor.setThrown(new String[] {"java.util.prefs.BackingStoreException"});
623 _cbtc.simpleThisConstructorInvocationAllowed(constr);
624 assertEquals("Should still be 1 error", 1, errors.size());
625 assertTrue("vd1 should have a value", vd1.hasValue());
626 assertTrue("vd2 should have a value", vd2.hasValue());
627 assertFalse("vd3 is not final, and thus should not have a value", vd3.hasValue());
628 assertEquals("thrown should have 1 element", 1, _cbtc._thrown.size());
629
630 }
631
632
633 public void testComplexThisConstructorInvocationNotAllowed() {
634 ComplexThisConstructorInvocation constr = new ComplexThisConstructorInvocation(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
635 _cbtc.complexThisConstructorInvocationNotAllowed(constr);
636 assertEquals("There should be 1 error", 1, errors.size());
637 assertEquals("Error message should be correct", "Constructor Invocations of this form are never allowed. A constructor invocation can appear here, but it must either be a super constructor invocation or have the form this(...)", errors.getLast().getFirst());
638 }
639
640
641 public void testSimpleSuperConstructorInvocationAllowed() {
642 //if current class does not have a super class, give an error
643 SimpleSuperConstructorInvocation constr = new SimpleSuperConstructorInvocation(SourceInfo.NONE, new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new IntegerLiteral(SourceInfo.NONE, 5)}));
644 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
645 assertEquals("Should be 1 error", 1, errors.size());
646 assertEquals("Error message should be correct", "The class i.like.monkey does not have a super class", errors.getLast().getFirst());
647
648 //if current class has a super class, but there isn't a constructor of the right form, give an error
649 _sd1.setSuperClass(_sd3);
650 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
651 assertEquals("Should be 2 errors", 2, errors.size());
652 assertEquals("Error message should be correct", "No constructor found in class zebra with signature: zebra(int).", errors.getLast().getFirst());
653
654 //if everything is right, should work with no errors
655 MethodData constructor = new MethodData("zebra", _publicMav, new TypeParameter[0], _sd3, new VariableData[] {new VariableData(SymbolData.INT_TYPE)}, new String[0], _sd3, null);
656 _sd3.addMethod(constructor);
657 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
658 assertEquals("Should still be 2 errors", 2, errors.size());
659
660 //if there is a constructor of the right form, but it throws an exception, the exception will be added to the _thrown list.
661 constructor.setThrown(new String[] {"java.util.prefs.BackingStoreException"});
662 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
663 assertEquals("Should still be 2 errors", 2, errors.size());
664 assertEquals("thrown should have 1 element", 1, _cbtc._thrown.size());
665
666 //if super class is an inner class, it must be static--if not, throw an error
667 constructor.setThrown(new String[0]);
668 _sd3.setOuterData(_sd5);
669 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
670 assertEquals("Should be 3 errors", 3, errors.size());
671 assertEquals("Error message should be correct", "zebra is a non-static inner class of elephant. Its constructor must be invoked from an instance of its outer class", errors.getLast().getFirst());
672
673
674 //if super class is a static inner class, no error
675 _sd3.addModifier("static");
676 _cbtc.simpleSuperConstructorInvocationAllowed(constr);
677 assertEquals("Should still be 3 errors", 3, errors.size());
678
679 }
680
681 public void testComplexSuperConstructorInvocationAllowed() {
682
683 //if enclosingResult is a PackageData, return null and add an error
684 ComplexSuperConstructorInvocation constr1 = new ComplexSuperConstructorInvocation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "nonExistant")), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
685 _cbtc.complexSuperConstructorInvocationAllowed(constr1);
686 assertEquals("Should be 1 error", 1, errors.size());
687 assertEquals("Error message should be correct", "Could not resolve symbol nonExistant", errors.getLast().getFirst());
688
689 //if the superclass is null, add error
690 ComplexSuperConstructorInvocation constr2 = new ComplexSuperConstructorInvocation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "zebra")), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
691 symbolTable.put("zebra", _sd3);
692 _sd3.setIsContinuation(false);
693 _sd3.setMav(_publicMav);
694 _cbtc.complexSuperConstructorInvocationAllowed(constr2);
695 assertEquals("Should be 2 errors", 2, errors.size());
696 assertEquals("Error message should be correct", "A qualified super constructor invocation can only be used to invoke the constructor of your super class from the context of its outer class. The class i.like.monkey does not have a super class, so you cannot do this here", errors.getLast().getFirst());
697
698 //if the superclass exists, but the outer data is null, throw error
699 _sd1.setSuperClass(_sd5);
700 _cbtc.complexSuperConstructorInvocationAllowed(constr2);
701 assertEquals("Should be 3 errors", 3, errors.size());
702 assertEquals("Error message should be correct", "A qualified super constructor invocation can only be used to invoke the constructor of your super class from the context of its outer class. The super class elephant does not have an outer class, so you cannot do this here", errors.getLast().getFirst());
703
704 //if the outer data of the super class does not match the name specified in the constructor, give error
705 _sd5.setOuterData(_sd3);
706 ComplexSuperConstructorInvocation constr3 = new ComplexSuperConstructorInvocation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "u.like.emu")), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
707 symbolTable.put("u.like.emu", _sd4);
708 _sd4.setPackage("u.like");
709 _sd4.setIsContinuation(false);
710 _sd4.setMav(_publicMav);
711 _cbtc.complexSuperConstructorInvocationAllowed(constr3);
712 assertEquals("Should be 4 errors", 4, errors.size());
713 assertEquals("Error message should be correct", "A qualified super constructor invocation can only be used to invoke the constructor of your super class from the context of its outer class. The class or interface u.like.emu is not the outer class of the super class elephant", errors.getLast().getFirst());
714
715 //if the enclosing is not an instance type, give an error
716 _cbtc.complexSuperConstructorInvocationAllowed(constr2);
717 assertEquals("Should be 5 errors", 5, errors.size());
718 assertEquals("Error message should be correct", "A qualified super constructor invocation can only be made from the context of an instance of the outer class of the super class. You have specified a type name", errors.getLast().getFirst());
719
720 //if it is an instance type but can't find constructor, give error
721 ComplexSuperConstructorInvocation constr4 = new ComplexSuperConstructorInvocation(SourceInfo.NONE, new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "var")), new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]));
722 _cbtc._vars.add(new VariableData("var", _publicMav, _sd3, true, _sd1));
723 _cbtc.complexSuperConstructorInvocationAllowed(constr4);
724 assertEquals("Should be 6 errors", 6, errors.size());
725 assertEquals("Error message should be correct", "No constructor found in class elephant with signature: elephant().", errors.getLast().getFirst());
726
727 //if it is an instance type and can match constructor, no problems--should add anything thrown to throws list
728 MethodData constructor = new MethodData("elephant", _publicMav, new TypeParameter[0], _sd5, new VariableData[0], new String[] {"java.util.prefs.BackingStoreException"}, _sd5, null);
729 _sd5.addMethod(constructor);
730 _cbtc.complexSuperConstructorInvocationAllowed(constr4);
731 assertEquals("Should still be 6 errors", 6, errors.size());
732 assertEquals("_thrown should now have 1 element", 1, _cbtc._thrown.size());
733
734 //if it is an instance type, but the super class is static, give an error
735 _sd5.addModifier("static");
736 _cbtc.complexSuperConstructorInvocationAllowed(constr4);
737 assertEquals("Should be 7 errors", 7, errors.size());
738 assertEquals("Error message should be correct", "A qualified super constructor invocation can only be used to invoke the constructor of a non-static super class from the context of its outer class. The super class elephant is a static inner class", errors.getLast().getFirst());
739 }
740
741
742 public void testImplicitSuperConstructor() {
743 BracedBody constr = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
744 //if current class has a super class, but there isn't a constructor of the right form, give an error
745 _sd1.setSuperClass(_sd3);
746 _cbtc.implicitSuperConstructor(constr);
747 assertEquals("Should be 1 error", 1, errors.size());
748 assertEquals("Error message should be correct", "You must invoke one of zebra's constructors here. You can either explicitly invoke one of its exisitng constructors or add a constructor with signature: zebra().",
749 errors.getLast().getFirst());
750
751 //if everything is right, should work with no errors
752 MethodData constructor = new MethodData("zebra", _publicMav, new TypeParameter[0], _sd3, new VariableData[0], new String[0], _sd3, null);
753 _sd3.addMethod(constructor);
754 _cbtc.implicitSuperConstructor(constr);
755 assertEquals("Should still be 1 error", 1, errors.size());
756
757 //if there is a constructor of the right form, but it throws an exception, the exception will be added to the _thrown list.
758 constructor.setThrown(new String[] {"java.util.prefs.BackingStoreException"});
759 _cbtc.implicitSuperConstructor(constr);
760 assertEquals("Should still be 1 error", 1, errors.size());
761 assertEquals("thrown should have 1 element", 1, _cbtc._thrown.size());
762
763 //if super class is an inner class, it must be static--if not, throw an error
764 constructor.setThrown(new String[0]);
765 _sd3.setOuterData(_sd5);
766 _cbtc.implicitSuperConstructor(constr);
767 assertEquals("Should be 2 errors", 2, errors.size());
768 assertEquals("Error message should be correct", "There is an implicit call to the constructor of zebra here, but zebra is a non-static inner class of elephant. Thus, you must explicitly invoke its constructor from an instance of its outer class", errors.getLast().getFirst());
769
770
771 //if super class is a static inner class, no error
772 _sd3.addModifier("static");
773 _cbtc.implicitSuperConstructor(constr);
774 assertEquals("Should still be 2 errors", 2, errors.size());
775 }
776 }
777 }
778
779
780
781
782