public void visitFunctionDeclaration(FunctionDeclarationContext function) {
    symbolTable.newScope();

    Function f = buildFunction(function);
    returnType = f.getReturnValueType();

    f.getArguments().stream().forEach(symbolTable::defineVariable);
    Type[] argTypes =
        f.getArguments()
            .stream()
            .flatMap(variable -> Stream.of(variable.getValueType().toAsmType()))
            .toArray(Type[]::new);
    String descriptor = Type.getMethodDescriptor(f.getReturnValueType().toAsmType(), argTypes);
    if ("main".equals(f.getName())) {
      if (!f.getArguments().isEmpty()) {
        throw new GenerationException("Main function must have zero arguments");
      }
      descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class));
    }
    method = writer.visitMethod(ACC_PUBLIC | ACC_STATIC, f.getName(), descriptor, null, null);

    BlockContext block = function.functionBody().block();
    visitBlock(block);
    checkReturn(block);

    method.visitMaxs(0, 0);
    method.visitEnd();

    symbolTable.dropScope();
  }
  private void visitBlock(BlockContext block) {
    symbolTable.newScope();

    for (BlockStatementContext blockStatement : block.blockStatement()) {
      visitBlockStatement(blockStatement);
    }

    symbolTable.dropScope();
  }