Clover coverage report - DynamicJava Test Coverage (dynamicjava-20120303-r5436)
Coverage timestamp: Sat Mar 3 2012 03:02:19 CST
file stats: LOC: 770   Methods: 35
NCLOC: 541   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
StatementChecker.java 27.9% 46.6% 62.9% 42.8%
coverage coverage
 1    /*
 2    * DynamicJava - Copyright (C) 1999-2001
 3    *
 4    * Permission is hereby granted, free of charge, to any person obtaining a
 5    * copy of this software and associated documentation files
 6    * (the "Software"), to deal in the Software without restriction, including
 7    * without limitation the rights to use, copy, modify, merge, publish,
 8    * distribute, sublicense, and/or sell copies of the Software, and to permit
 9    * persons to whom the Software is furnished to do so, subject to the
 10    * following conditions:
 11    * The above copyright notice and this permission notice shall be included
 12    * in all copies or substantial portions of the Software.
 13    *
 14    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 15    * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 16    * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 17    * IN NO EVENT SHALL DYADE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 19    * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 20    * DEALINGS IN THE SOFTWARE.
 21    *
 22    * Except as contained in this notice, the name of Dyade shall not be
 23    * used in advertising or otherwise to promote the sale, use or other
 24    * dealings in this Software without prior written authorization from
 25    * Dyade.
 26    *
 27    */
 28   
 29    /*BEGIN_COPYRIGHT_BLOCK
 30    *
 31    * This file is part of DrJava. Download the current version of this project:
 32    * http://sourceforge.net/projects/drjava/ or http://www.drjava.org/
 33    *
 34    * DrJava Open Source License
 35    *
 36    * Copyright (C) 2001-2010 JavaPLT group at Rice University (drjava@rice.edu)
 37    * All rights reserved.
 38    *
 39    * Developed by: Java Programming Languages Team
 40    * Rice University
 41    * http://www.cs.rice.edu/~javaplt/
 42    *
 43    * Permission is hereby granted, free of charge, to any person obtaining a
 44    * copy of this software and associated documentation files (the "Software"),
 45    * to deal with the Software without restriction, including without
 46    * limitation the rights to use, copy, modify, merge, publish, distribute,
 47    * sublicense, and/or sell copies of the Software, and to permit persons to
 48    * whom the Software is furnished to do so, subject to the following
 49    * conditions:
 50    *
 51    * - Redistributions of source code must retain the above copyright
 52    * notice, this list of conditions and the following disclaimers.
 53    * - Redistributions in binary form must reproduce the above copyright
 54    * notice, this list of conditions and the following disclaimers in the
 55    * documentation and/or other materials provided with the distribution.
 56    * - Neither the names of DrJava, the JavaPLT, Rice University, nor the
 57    * names of its contributors may be used to endorse or promote products
 58    * derived from this Software without specific prior written permission.
 59    * - Products derived from this software may not be called "DrJava" nor
 60    * use the term "DrJava" as part of their names without prior written
 61    * permission from the JavaPLT group. For permission, write to
 62    * drjava@rice.edu.
 63    *
 64    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 65    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 66    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 67    * THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 68    * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 69    * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 70    * OTHER DEALINGS WITH THE SOFTWARE.
 71    *
 72    END_COPYRIGHT_BLOCK*/
 73   
 74    package edu.rice.cs.dynamicjava.interpreter;
 75   
 76    import java.util.Collections;
 77    import java.util.Set;
 78    import java.util.HashSet;
 79    import java.util.List;
 80    import java.util.LinkedList;
 81    import edu.rice.cs.plt.iter.IterUtil;
 82    import edu.rice.cs.plt.tuple.Pair;
 83    import edu.rice.cs.plt.tuple.Option;
 84    import edu.rice.cs.plt.lambda.Lambda;
 85   
 86    import koala.dynamicjava.tree.*;
 87    import koala.dynamicjava.tree.tiger.*;
 88    import koala.dynamicjava.tree.visitor.*;
 89    import koala.dynamicjava.interpreter.error.ExecutionError;
 90    import koala.dynamicjava.interpreter.TypeUtil;
 91   
 92    import edu.rice.cs.dynamicjava.Options;
 93    import edu.rice.cs.dynamicjava.symbol.*;
 94    import edu.rice.cs.dynamicjava.symbol.type.*;
 95    import edu.rice.cs.dynamicjava.symbol.TypeSystem.*;
 96   
 97    import static koala.dynamicjava.interpreter.NodeProperties.*;
 98   
 99    import static edu.rice.cs.plt.debug.DebugUtil.debug;
 100   
 101    /**
 102    * Traverses the given statements and declarations, performing static checks along the way.
 103    * A variety of properties (from {@code NodeProperties}) are set on certain nodes. In addition to
 104    * those documented in {@link ExpressionChecker}, the following are set:<ul>
 105    * <li>VARIABLE on all {@link VariableDeclaration}s and {@link FormalParameter}s</li>
 106    * <li>TYPE_VARIABLE on all {@link TypeParameter}s</li>
 107    * <li>ERASED_TYPE on all {@link CatchStatement}s, {@link VariableDeclaration}s, and
 108    * {@link MethodDeclaration}s</li>
 109    * <li>DJClASS on class declarations</li>
 110    * </ul>
 111    * Throws an ExecutionError if an error is found.
 112    */
 113    // TODO: Handle non-literal constant expressions
 114    public class StatementChecker extends AbstractVisitor<TypeContext> implements Lambda<Node, TypeContext> {
 115   
 116    private final TypeContext context;
 117    private final Options opt;
 118    private final TypeSystem ts; // contained by opt, but this is a shortcut for easy reference
 119   
 120  2967 public StatementChecker(TypeContext ctx, Options options) {
 121  2967 context = ctx;
 122  2967 opt = options;
 123  2967 ts = opt.typeSystem();
 124    }
 125   
 126  0 public TypeContext value(Node n) { return n.acceptVisitor(this); }
 127   
 128  1314 public TypeContext checkList(Iterable<? extends Node> l) {
 129  1314 ExecutionError error = null;
 130  1314 TypeContext c = context;
 131  1314 for (Node n : l) {
 132  1890 try { c = n.acceptVisitor(new StatementChecker(c, opt)); }
 133    catch (ExecutionError e) {
 134  27 if (hasErrorContext(n)) { c = getErrorContext(n); }
 135  47 if (error == null) { error = e; }
 136    }
 137    }
 138  47 if (error != null) { throw error; }
 139  1267 return c;
 140    }
 141   
 142  1005 private Type checkType(Expression exp) { return new ExpressionChecker(context, opt).check(exp); }
 143   
 144  774 private Type checkType(Expression exp, Type expected) {
 145  774 return new ExpressionChecker(context, opt).check(exp, expected);
 146    }
 147   
 148  0 @SuppressWarnings("unused") private Iterable<Type> checkTypes(Iterable<? extends Expression> l) {
 149  0 return new ExpressionChecker(context, opt).checkList(l);
 150    }
 151   
 152  667 private Type checkTypeName(TypeName t) {
 153  667 return new TypeNameChecker(context, opt).check(t);
 154    }
 155   
 156   
 157    /** Creates a new context in the given package */
 158  0 @Override public TypeContext visit(PackageDeclaration node) {
 159  0 return context.setPackage(node.getName());
 160    }
 161   
 162    /** Creates a new context with the given import */
 163  9 @Override public TypeContext visit(ImportDeclaration node) {
 164  9 if (node.isStatic()) {
 165   
 166  0 if (node.isPackage()) {
 167    // static on-demand import
 168  0 ClassType t = resolveClassName(node.getName(), node);
 169  0 if (t == null) {
 170  0 setErrorStrings(node, node.getName());
 171  0 throw new ExecutionError("undefined.class", node);
 172    }
 173  0 return context.importStaticMembers(t.ofClass());
 174    }
 175   
 176    else {
 177    // static member import
 178  0 Pair<String, String> split = splitName(node.getName());
 179  0 if (split.first() == null) {
 180  0 setErrorStrings(node, node.getName());
 181  0 throw new ExecutionError("undefined.name", node);
 182    }
 183  0 ClassType t = resolveClassName(split.first(), node);
 184  0 if (t == null) {
 185  0 setErrorStrings(node, node.getName());
 186  0 throw new ExecutionError("undefined.class", node);
 187    }
 188  0 String member = split.second();
 189  0 TypeContext result = context;
 190  0 if (ts.containsStaticField(t, member, context.accessModule())) {
 191  0 result = result.importField(t.ofClass(), member);
 192    }
 193  0 if (ts.containsStaticMethod(t, member, context.accessModule())) {
 194  0 result = result.importMethod(t.ofClass(), member);
 195    }
 196  0 if (ts.containsStaticClass(t, member, context.accessModule())) {
 197  0 result = result.importMemberClass(t.ofClass(), member);
 198    }
 199  0 if (result == context) {
 200  0 setErrorStrings(node, node.getName());
 201  0 throw new ExecutionError("undefined.name", node);
 202    }
 203  0 return result;
 204    }
 205   
 206    }
 207    else {
 208   
 209  9 if (node.isPackage()) {
 210    // on-demand import
 211  9 ClassType t = resolveClassName(node.getName(), node);
 212  9 if (t == null) { return context.importTopLevelClasses(node.getName()); }
 213  0 else { return context.importMemberClasses(t.ofClass()); }
 214    }
 215   
 216    else {
 217    // class import
 218  0 Pair<String, String> split = splitName(node.getName());
 219  0 if (split.first() != null) {
 220  0 ClassType t = resolveClassName(split.first(), node);
 221  0 if (t != null) {
 222  0 if (ts.containsClass(t, split.second(), context.accessModule())) {
 223  0 return context.importMemberClass(t.ofClass(), split.second());
 224    }
 225    else {
 226  0 setErrorStrings(node, ts.typePrinter().print(t), split.second());
 227  0 throw new ExecutionError("no.such.inner.class", node);
 228    }
 229    }
 230    }
 231  0 try {
 232  0 DJClass c = context.getTopLevelClass(node.getName(), ts);
 233  0 if (c == null) {
 234  0 setErrorStrings(node, node.getName());
 235  0 throw new ExecutionError("undefined.class", node);
 236    }
 237  0 else { return context.importTopLevelClass(c); }
 238    }
 239    catch (AmbiguousNameException e) {
 240  0 setErrorStrings(node, node.getName());
 241  0 throw new ExecutionError("ambiguous.name", node);
 242    }
 243    }
 244   
 245    }
 246    }
 247   
 248  9 private ClassType resolveClassName(String name, Node node) {
 249  9 String topLevelName = "";
 250  9 ClassType result = null;
 251  9 boolean first = true;
 252  9 for (String piece : name.split("\\.")) {
 253  18 if (result == null) {
 254  9 if (!first) { topLevelName += "."; }
 255  18 first = false;
 256  18 topLevelName += piece;
 257  18 try {
 258  18 DJClass c = context.getTopLevelClass(topLevelName, ts);
 259  18 result = (c == null) ? null : ts.makeClassType(c);
 260    }
 261    catch (AmbiguousNameException e) {
 262  0 setErrorStrings(node, topLevelName);
 263  0 throw new ExecutionError("ambiguous.name", node);
 264    }
 265    }
 266    else {
 267  0 try { result = ts.lookupClass(result, piece, IterUtil.<Type>empty(), context.accessModule()); }
 268  0 catch (InvalidTypeArgumentException e) { throw new RuntimeException("can't create raw type"); }
 269    catch (UnmatchedLookupException e) {
 270  0 setErrorStrings(node, ts.typePrinter().print(result), piece);
 271  0 if (e.matches() > 1) { throw new ExecutionError("ambiguous.inner.class", node); }
 272  0 else { throw new ExecutionError("no.such.inner.class", node); }
 273    }
 274    }
 275    }
 276  9 return result;
 277    }
 278   
 279  0 private Pair<String, String> splitName(String name) {
 280  0 int dot = name.lastIndexOf('.');
 281  0 if (dot == -1) { return Pair.make(null, name); }
 282  0 else { return Pair.make(name.substring(0, dot), name.substring(dot+1)); }
 283    }
 284   
 285    /** Checks the declaration's initializer and creates a new context */
 286  660 @Override public TypeContext visit(VariableDeclaration node) {
 287  660 if (node.getType() == null) {
 288    // We infer the variable's type. We can assume the initializer is non-null.
 289  0 Type initT = checkType(node.getInitializer());
 290  0 LocalVariable v = new LocalVariable(node.getName(), initT, node.getModifiers().isFinal());
 291  0 setVariable(node, v);
 292  0 setErasedType(node, ts.erasedClass(initT));
 293  0 return new LocalContext(context, v);
 294    }
 295    else {
 296  660 boolean initialized = (node.getInitializer() != null);
 297  660 Type t = checkTypeName(node.getType());
 298  657 LocalVariable v = new LocalVariable(node.getName(), t, initialized && node.getModifiers().isFinal());
 299  657 setVariable(node, v);
 300  657 setErasedType(node, ts.erasedClass(t));
 301  657 TypeContext newContext = new LocalContext(context, v);
 302   
 303  657 if (initialized) {
 304  639 try {
 305  639 Type initT = checkType(node.getInitializer(), t);
 306  628 try {
 307  628 Expression newInit = ts.assign(t, node.getInitializer());
 308  612 node.setInitializer(newInit);
 309    }
 310    catch (UnsupportedConversionException e) {
 311  16 TypePrinter printer = ts.typePrinter();
 312  16 setErrorStrings(node, printer.print(initT), printer.print(t));
 313  16 throw new ExecutionError("assignment.types", node);
 314    }
 315    }
 316  27 catch (ExecutionError e) { setErrorContext(node, newContext); throw e; }
 317    }
 318  630 return newContext;
 319    }
 320    }
 321   
 322  89 @Override public TypeContext visit(ClassDeclaration node) {
 323  89 return handleTypeDeclaration(node);
 324    }
 325   
 326  0 @Override public TypeContext visit(InterfaceDeclaration node) {
 327  0 return handleTypeDeclaration(node);
 328    }
 329   
 330  89 private TypeContext handleTypeDeclaration(TypeDeclaration node) {
 331  89 TreeClassLoader loader = new TreeClassLoader(context.getClassLoader(), opt);
 332  89 DJClass c = new TreeClass(context.makeClassName(node.getName()), null, context.accessModule(), node, loader, opt);
 333  89 setDJClass(node, c);
 334  89 ClassChecker classChecker = new ClassChecker(c, loader, context, opt);
 335  89 classChecker.initializeClassSignatures(node);
 336  89 classChecker.checkSignatures(node);
 337  89 classChecker.checkBodies(node);
 338  89 return new LocalContext(context, loader, c);
 339    }
 340   
 341    /**
 342    * Visits a MethodDeclaration. Treated as a local function (class methods are handled by
 343    * ClassMemberChecker).
 344    */
 345  183 @Override public TypeContext visit(MethodDeclaration node) {
 346  183 LocalFunction f = new LocalFunction(node);
 347   
 348  183 TypeContext sigContext = new FunctionSignatureContext(context, f);
 349  183 TypeNameChecker sigChecker = new TypeNameChecker(sigContext, opt);
 350   
 351  183 Iterable<TypeParameter> tparams = node.getTypeParams().unwrap(Collections.<TypeParameter>emptyList());
 352  183 sigChecker.checkTypeParameters(tparams);
 353   
 354  183 Type returnT = sigChecker.check(node.getReturnType());
 355  183 setErasedType(node, ts.erasedClass(returnT));
 356  183 for (FormalParameter p : node.getParameters()) {
 357  183 Type t = sigChecker.check(p.getType());
 358  183 setVariable(p, new LocalVariable(p.getName(), t, p.getModifiers().isFinal()));
 359    }
 360  0 for (ReferenceTypeName n : node.getExceptions()) { sigChecker.check(n); }
 361   
 362  183 if (node.getBody() == null) {
 363  0 setErrorStrings(node, node.getName());
 364  0 throw new ExecutionError("missing.method.body", node);
 365    }
 366  183 TypeContext bodyContext = new FunctionContext(sigContext, f);
 367  183 node.getBody().acceptVisitor(new StatementChecker(bodyContext, opt));
 368   
 369  183 return new LocalContext(context, f);
 370    }
 371   
 372   
 373    /**
 374    * Visits a WhileStatement. JLS 14.12.
 375    */
 376  3 @Override public TypeContext visit(WhileStatement node) {
 377  3 checkType(node.getCondition());
 378  3 try {
 379  3 Expression exp = ts.makePrimitive(node.getCondition());
 380  3 if (!(getType(exp) instanceof BooleanType)) {
 381  0 throw new ExecutionError("condition.type", node);
 382    }
 383  3 node.setCondition(exp);
 384    }
 385    catch (UnsupportedConversionException e) {
 386  0 throw new ExecutionError("condition.type", node);
 387    }
 388   
 389  3 node.getBody().acceptVisitor(this);
 390  3 return context;
 391    }
 392   
 393    /**
 394    * Visits a DoStatement. JLS 14.13.
 395    */
 396  3 @Override public TypeContext visit(DoStatement node) {
 397  3 node.getBody().acceptVisitor(this);
 398  3 checkType(node.getCondition());
 399  3 try {
 400  3 Expression exp = ts.makePrimitive(node.getCondition());
 401  3 if (!(getType(exp) instanceof BooleanType)) {
 402  0 throw new ExecutionError("condition.type", node);
 403    }
 404  3 node.setCondition(exp);
 405    }
 406    catch (UnsupportedConversionException e) {
 407  0 throw new ExecutionError("condition.type", node);
 408    }
 409   
 410  3 return context;
 411    }
 412   
 413    /**
 414    * Visits a ForStatement. JLS 14.4.1.
 415    */
 416  111 @Override public TypeContext visit(ForStatement node) {
 417  111 TypeContext newContext = context;
 418  111 if (node.getInitialization() != null) { newContext = checkList(node.getInitialization()); }
 419  111 StatementChecker checker = new StatementChecker(newContext, opt);
 420   
 421  111 if (node.getCondition() != null) {
 422  111 checker.checkType(node.getCondition());
 423  111 try {
 424  111 Expression exp = ts.makePrimitive(node.getCondition());
 425  111 if (!(getType(exp) instanceof BooleanType)) {
 426  0 throw new ExecutionError("condition.type", node);
 427    }
 428  111 node.setCondition(exp);
 429    }
 430    catch (UnsupportedConversionException e) {
 431  0 throw new ExecutionError("condition.type", node);
 432    }
 433    }
 434   
 435  111 if (node.getUpdate() != null) { checker.checkList(node.getUpdate()); }
 436   
 437  111 node.getBody().acceptVisitor(checker);
 438  111 return context; // We do *not* return newContext
 439    }
 440   
 441    /**
 442    * Visits a ForEachStatement. JLS 14.14.2.
 443    */
 444  7 @Override public TypeContext visit(ForEachStatement node) {
 445  7 FormalParameter p = node.getParameter();
 446  7 Type paramT = checkTypeName(p.getType());
 447  7 LocalVariable var = setVariable(p, new LocalVariable(p.getName(), paramT, p.getModifiers().isFinal()));
 448  7 TypeContext newContext = new LocalContext(context, var);
 449  7 Type collType = checkType(node.getCollection());
 450   
 451  7 if (ts.isArray(collType)) {
 452  4 Type elementType = ts.arrayElementType(collType);
 453  4 if (!ts.isAssignable(paramT, elementType)) {
 454  0 TypePrinter printer = ts.typePrinter();
 455  0 setErrorStrings(node, printer.print(elementType), printer.print(paramT));
 456  0 throw new ExecutionError("assignment.types", node);
 457    }
 458    }
 459  3 else if (ts.isIterable(collType)) {
 460  3 try {
 461  3 MethodInvocation iteratorInv = ts.lookupMethod(node.getCollection(), "iterator", IterUtil.<Type>empty(),
 462    IterUtil.<Expression>empty(), Option.<Type>none(),
 463    context.accessModule());
 464   
 465   
 466  3 Expression getIterator = TypeUtil.makeEmptyExpression(node.getCollection());
 467  3 setType(getIterator, iteratorInv.returnType());
 468  3 MethodInvocation nextInv = ts.lookupMethod(getIterator, "next", IterUtil.<Type>empty(),
 469    IterUtil.<Expression>empty(), Option.<Type>none(),
 470    context.accessModule());
 471   
 472  3 if (!ts.isAssignable(paramT, nextInv.returnType())) {
 473  0 TypePrinter printer = ts.typePrinter();
 474  0 setErrorStrings(node, printer.print(nextInv.returnType()), printer.print(paramT));
 475  0 throw new ExecutionError("assignment.types", node);
 476    }
 477    }
 478  0 catch (TypeSystemException e) { throw new RuntimeException("ts.isIterable() lied"); }
 479    }
 480    else {
 481  0 throw new ExecutionError("iterable.type", node);
 482    }
 483   
 484  7 node.getBody().acceptVisitor(new StatementChecker(newContext, opt));
 485  7 return context; // We do *not* return newContext
 486    }
 487   
 488    /**
 489    * Visits an IfThenStatement. JLS 14.9.
 490    */
 491  191 @Override public TypeContext visit(IfThenStatement node) {
 492  191 checkType(node.getCondition());
 493  191 try {
 494  191 Expression exp = ts.makePrimitive(node.getCondition());
 495  189 if (!(getType(exp) instanceof BooleanType)) {
 496  0 throw new ExecutionError("condition.type", node);
 497    }
 498  189 node.setCondition(exp);
 499    }
 500    catch (UnsupportedConversionException e) {
 501  2 throw new ExecutionError("condition.type", node);
 502    }
 503   
 504  189 node.getThenStatement().acceptVisitor(this);
 505  189 return context;
 506    }
 507   
 508    /**
 509    * Visits an IfThenElseStatement. JLS 14.9.
 510    */
 511  6 @Override public TypeContext visit(IfThenElseStatement node) {
 512  6 checkType(node.getCondition());
 513  6 try {
 514  6 Expression exp = ts.makePrimitive(node.getCondition());
 515  6 if (!(getType(exp) instanceof BooleanType)) {
 516  0 throw new ExecutionError("condition.type", node);
 517    }
 518  6 node.setCondition(exp);
 519    }
 520    catch (UnsupportedConversionException e) {
 521  0 throw new ExecutionError("condition.type", node);
 522    }
 523   
 524  6 node.getThenStatement().acceptVisitor(this);
 525  6 node.getElseStatement().acceptVisitor(this);
 526  6 return context;
 527    }
 528   
 529    /**
 530    * Visits a SwitchStatement. JLS 14.11.
 531    */
 532  0 @Override public TypeContext visit(SwitchStatement node) {
 533  0 Type t = checkType(node.getSelector());
 534  0 boolean switchEnum = ts.isEnum(t);
 535  0 if (!switchEnum) {
 536  0 try {
 537  0 Expression exp = ts.makePrimitive(node.getSelector());
 538  0 if (!(getType(exp) instanceof IntegralType) || (getType(exp) instanceof LongType)) {
 539  0 setErrorStrings(node, ts.typePrinter().print(t));
 540  0 throw new ExecutionError("selector.type", node);
 541    }
 542  0 node.setSelector(exp);
 543  0 t = getType(exp);
 544    }
 545    catch (UnsupportedConversionException e) {
 546  0 throw new ExecutionError("selector.type", node);
 547    }
 548    }
 549   
 550  0 Set<Object> values = new HashSet<Object>();
 551  0 boolean hasDefault = false;
 552  0 for (SwitchBlock bk : node.getBindings()) {
 553    /* To be fully correct, the context used here should follow the following scoping rules (JLS 6.3):
 554    - A local variable is in scope for the rest of the switch statement's body -- it "falls through"
 555    - A local class is *only* in scope in its SwitchBlock -- it does not "fall through"
 556    This is a mess. For now we just follow a no-fall-through approach. */
 557  0 if (bk.getExpression() == null) {
 558  0 if (hasDefault) { throw new ExecutionError("duplicate.switch.case", node); }
 559  0 hasDefault = true;
 560    }
 561  0 else if (switchEnum) {
 562  0 DJField val = new ExpressionChecker(context, opt).checkEnumSwitchCase(bk.getExpression(), t);
 563  0 if (values.contains(val)) {
 564  0 throw new ExecutionError("duplicate.switch.case", bk);
 565    }
 566  0 values.add(val);
 567    }
 568    else {
 569  0 Expression exp = bk.getExpression();
 570  0 checkType(exp);
 571  0 if (!hasValue(exp) || getValue(exp) == null) {
 572  0 throw new ExecutionError("invalid.constant", exp);
 573    }
 574  0 if (!ts.isAssignable(t, getType(exp), getValue(exp))) {
 575  0 setErrorStrings(exp, ts.typePrinter().print(getType(exp)));
 576  0 throw new ExecutionError("switch.label.type", exp);
 577    }
 578  0 if (values.contains(getValue(exp))) {
 579  0 throw new ExecutionError("duplicate.switch.case", bk);
 580    }
 581  0 values.add(getValue(exp));
 582    }
 583   
 584  0 if (bk.getStatements() != null) { checkList(bk.getStatements()); }
 585    }
 586   
 587  0 return context;
 588    }
 589   
 590    /**
 591    * Visits a SwitchBlock. JLS 14.11.
 592    */
 593  0 @Override public TypeContext visit(SwitchBlock node) {
 594  0 if (node.getExpression() != null) { checkType(node.getExpression()); }
 595  0 if (node.getStatements() != null) { checkList(node.getStatements()); }
 596  0 return context;
 597    }
 598   
 599    /**
 600    * Visits a LabeledStatement. JLS 14.7.
 601    */
 602  0 @Override public TypeContext visit(LabeledStatement node) {
 603  0 return node.getStatement().acceptVisitor(this);
 604    }
 605   
 606    /**
 607    * Visits a TryStatement. JLS 14.20.
 608    */
 609  0 @Override public TypeContext visit(TryStatement node) {
 610  0 List<Type> caughtTypes = new LinkedList<Type>();
 611  0 for (CatchStatement c : node.getCatchStatements()) {
 612  0 FormalParameter p = c.getException();
 613  0 Type caughtT = checkTypeName(p.getType());
 614  0 if (!ts.isAssignable(TypeSystem.THROWABLE, caughtT)) {
 615  0 setErrorStrings(c, ts.typePrinter().print(caughtT));
 616  0 throw new ExecutionError("catch.type", c);
 617    }
 618  0 if (!ts.isReifiable(caughtT)) {
 619  0 throw new ExecutionError("reifiable.type", c);
 620    }
 621  0 setVariable(p, new LocalVariable(p.getName(), caughtT, p.getModifiers().isFinal()));
 622  0 setErasedType(c, ts.erasedClass(caughtT));
 623  0 caughtTypes.add(caughtT);
 624    }
 625   
 626  0 TypeContext tryContext = new TryBlockContext(context, caughtTypes);
 627  0 node.getTryBlock().acceptVisitor(new StatementChecker(tryContext, opt));
 628   
 629  0 for (CatchStatement c : node.getCatchStatements()) {
 630  0 TypeContext catchContext = new LocalContext(context, getVariable(c.getException()));
 631  0 c.getBlock().acceptVisitor(new StatementChecker(catchContext, opt));
 632    }
 633   
 634  0 if (node.getFinallyBlock() != null) { node.getFinallyBlock().acceptVisitor(this); }
 635   
 636  0 return context;
 637    }
 638   
 639    /**
 640    * Visits a ThrowStatement. JLS 14.18.
 641    */
 642  183 @Override public TypeContext visit(ThrowStatement node) {
 643  183 Type thrown = checkType(node.getExpression());
 644  183 if (!ts.isAssignable(TypeSystem.THROWABLE, thrown)) {
 645  0 setErrorStrings(node, ts.typePrinter().print(thrown));
 646  0 throw new ExecutionError("throw.type", node);
 647    }
 648  183 else if (ts.isAssignable(TypeSystem.EXCEPTION, thrown)) {
 649  0 boolean valid = false;
 650  0 Iterable<Type> allowed = IterUtil.compose(TypeSystem.RUNTIME_EXCEPTION,
 651    context.getDeclaredThrownTypes());
 652  0 for (Type t : allowed) {
 653  0 if (ts.isAssignable(t, thrown)) { valid = true; break; }
 654    }
 655  0 if (!valid) {
 656  0 setErrorStrings(node, ts.typePrinter().print(thrown));
 657  0 throw new ExecutionError("uncaught.exception", node);
 658    }
 659    }
 660  183 return context;
 661    }
 662   
 663    /**
 664    * Visits a ReturnStatement
 665    */
 666  135 @Override public TypeContext visit(ReturnStatement node) {
 667  135 Type expected = context.getReturnType();
 668  0 if (expected == null) { throw new ExecutionError("return.not.allowed", node); }
 669   
 670  135 if (node.getExpression() == null) {
 671  0 if (!expected.equals(TypeSystem.VOID)) {
 672  0 TypePrinter printer = ts.typePrinter();
 673  0 setErrorStrings(node, printer.print(TypeSystem.VOID), printer.print(expected));
 674  0 throw new ExecutionError("return.type", node);
 675    }
 676    }
 677    else {
 678  135 checkType(node.getExpression(), expected);
 679  135 try {
 680  135 Expression newExp = ts.assign(expected, node.getExpression());
 681  135 node.setExpression(newExp);
 682    }
 683    catch (UnsupportedConversionException e) {
 684  0 TypePrinter printer = ts.typePrinter();
 685  0 setErrorStrings(node, printer.print(getType(node.getExpression())), printer.print(expected));
 686  0 throw new ExecutionError("return.type", node);
 687    }
 688    }
 689   
 690  135 return context;
 691    }
 692   
 693    /**
 694    * Visits an AssertStatement. JLS 14.10.
 695    */
 696  0 @Override public TypeContext visit(AssertStatement node) {
 697  0 checkType(node.getCondition());
 698  0 try {
 699  0 Expression exp = ts.makePrimitive(node.getCondition());
 700  0 if (!(getType(exp) instanceof BooleanType)) {
 701  0 throw new ExecutionError("condition.type", node);
 702    }
 703  0 node.setCondition(exp);
 704    }
 705    catch (UnsupportedConversionException e) {
 706  0 throw new ExecutionError("condition.type", node);
 707    }
 708   
 709  0 if (node.getFailString() != null) {
 710  0 Type failType = checkType(node.getFailString());
 711  0 if (failType instanceof VoidType) { throw new ExecutionError("assertion.fail.type", node); }
 712    }
 713   
 714  0 return context;
 715    }
 716   
 717    /**
 718    * Visits a SynchronizedStatement. JLS 14.19.
 719    */
 720  0 @Override public TypeContext visit(SynchronizedStatement node) {
 721  0 Type lockT = checkType(node.getLock());
 722  0 if (!ts.isReference(lockT)) { throw new ExecutionError("lock.type", node); }
 723  0 node.getBody().acceptVisitor(this);
 724  0 return context;
 725    }
 726   
 727    /**
 728    * Visits a BlockStatement
 729    * @param node the node to visit
 730    */
 731  474 @Override public TypeContext visit(BlockStatement node) {
 732  474 checkList(node.getStatements());
 733  474 return context;
 734    }
 735   
 736  1 @Override public TypeContext visit(EmptyStatement node) {
 737  1 return context;
 738    }
 739   
 740  0 @Override public TypeContext visit(BreakStatement node) {
 741  0 return context; // TODO: check control-flow context, labels
 742    }
 743   
 744  0 @Override public TypeContext visit(ContinueStatement node) {
 745  0 return context; // TODO: check control-flow context, labels
 746    }
 747   
 748  501 @Override public TypeContext visit(ExpressionStatement node) {
 749  501 if (node.getExpression() instanceof SimpleAssignExpression &&
 750    !opt.requireVariableType() && (node.getHasSemicolon() || !opt.requireSemicolon())) {
 751  235 SimpleAssignExpression assign = (SimpleAssignExpression) node.getExpression();
 752  235 if (assign.getLeftExpression() instanceof AmbiguousName) {
 753  235 AmbiguousName ambigName = (AmbiguousName) assign.getLeftExpression();
 754  235 if (ambigName.getIdentifiers().size() == 1) {
 755  235 String name = ambigName.getRepresentation();
 756  235 if (!context.variableExists(name, opt.typeSystem())) {
 757  0 Node decl = new VariableDeclaration(ModifierSet.make(), null, name, assign.getRightExpression(),
 758    node.getSourceInfo());
 759  0 setStatementTranslation(node, decl);
 760  0 return decl.acceptVisitor(this);
 761    }
 762    }
 763    }
 764    }
 765    // all other cases that don't match the nested ifs:
 766  501 checkType(node.getExpression());
 767  486 return context;
 768    }
 769   
 770    }