Search code examples
javacompiler-constructionjavacc

JavaCC Generates Dead Code


I'm using JavaCC for the first time and I'm noticing it's generating a lot of dead code. There are many lines that look like (pardon the spacing, it's automatic):

{if ("" != null) return result;}
    throw new Error("Missing return statement in function");
  }

Is it possible to avoid generating this dead code? It causes tens of compiler warnings that are hopefully avoidable.

Thanks!

Here's a full minimal example from the .jj file:

Statement UseStatement():
{
    String dbName;
}
{
    <USE> dbName=DbObjectName()
    {
        return new UseStatement(dbName);
    }
}

Which generates:

final public Statement UseStatement() throws ParseException {String dbName;
    jj_consume_token(USE);
    dbName = DbObjectName();
{if ("" != null) return new UseStatement(dbName);}
    throw new Error("Missing return statement in function");
  }

Additionally JavaCC is generating a ParserTokenManager file which throws a TokenMgrError - but the code doesn't compile. It declares a protected int curChar where it should declare it a char. Googling this phrase shows many examples where it is correctly declared a char - is this just a lot of people hand-editing the result?


Solution

  • I went to the source and to prevent the dead code you have to trigger Options.isLegacyExceptionHandling.

      // Add if statement to prevent subsequent code generated
      // from being dead code.
      // NB: eclipse now detects 'if (true)' as dead code, so use the more complicated
      // 'if ("" != null)'
      if (inAction  && (Options.isLegacyExceptionHandling()) ) {
        t.image = "{if (\"\" != null) return";
        jumpPatched = true;
      }
    

    which then triggers:

    if (p.isJumpPatched() && !voidReturn) {
      if (isJavaDialect) {
        // TODO :: I don't think we need to throw an Error/Exception to mark that a return statement is missing as the compiler will flag this error automatically
        if (Options.isLegacyExceptionHandling()) {
            codeGenerator.genCodeLine("    throw new "+(Options.isLegacyExceptionHandling() ? "Error" : "RuntimeException")+"(\"Missing return statement in function\");");
        }
      } else {
        codeGenerator.genCodeLine("    throw \"Missing return statement in function\";");
      }
    }
    

    https://github.com/javacc/javacc/blob/e38cbdb1db7ca8bd66f892859fef88b4876e69ba/src/main/javacc/JavaCC.jj#L2771-L2779

    https://github.com/javacc/javacc/blob/2ac628df1f899fdf6acf1f87fad313b6797085f7/src/main/java/org/javacc/parser/ParseEngine.java#L707-L712

    Legacy exception handling is a derived option that is false only when JAVA_TEMPLATE_TYPE=modern. The only way to get it set correctly is to include it in the options block in the .jj file as so:

    options {
        JAVA_TEMPLATE_TYPE="modern";
    }
    

    In theory it is also settable via the command line options, but as of this writing it's actually impossible to set the derived option before the command line arguments are parsed (#25)