@Override
  public void visitCleanupBlock(CleanupBlock block) {
    for (Block b : method.getBlocks()) {
      // call addBlockListeners() here, as this method will already copy the contents of the blocks,
      // so we need to transform the block listeners here as they won't be copied in visitMethodAgain where we normally add them
      addBlockListeners(b);
      if (b == block) break;
      moveVariableDeclarations(b.getAst(), method.getStatements());
    }

    VariableExpression featureThrowableVar =
        new VariableExpression(SpockNames.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable);
    method.getStatements().add(createVariableDeclarationStatement(featureThrowableVar));

    List<Statement> featureStats = new ArrayList<>();
    for (Block b : method.getBlocks()) {
      if (b == block) break;
      featureStats.addAll(b.getAst());
    }

    CatchStatement featureCatchStat = createThrowableAssignmentAndRethrowCatchStatement(featureThrowableVar);
    VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
    List<Statement> cleanupStats = asList(
        new ExpressionStatement(new DeclarationExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), ConstantExpression.NULL)),
        createCleanupTryCatch(block, featureThrowableVar, failedBlock));

    TryCatchStatement tryFinally =
        new TryCatchStatement(
            new BlockStatement(featureStats, null),
            new BlockStatement(cleanupStats, null));
    tryFinally.addCatch(featureCatchStat);

    method.getStatements().add(tryFinally);

    // a cleanup-block may only be followed by a where-block and filter-block, whose
    // statements are copied to newly generated methods rather than
    // the original method
    movedStatsBackToMethod = true;
  }
A Spec visitor responsible for most of the rewriting of a Spec's AST.

@author Peter Niederwieser
spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java