Repository: neovim/neovim
File: src/nvim/autocmd.c (C)

Code before:
  multiqueue_put(deferred_autocmd_events, deferred_event, evdata);
}

/// Execute a deferred autocommand event
static void deferred_event(void **argv)
{
  DeferredEvent *e = argv[0];
  event_T event = e->event;
  char *fname = e->fname;
  char *fname_io = e->fname_io;
  int group = e->group;
  exarg_T *eap = e->eap;
  Object *data = e->data;

  Error err = ERROR_INIT;
  buf_T *buf = find_buffer_by_handle(e->buf, &err);
  if (buf) {
    // set v:event with event data (if any)
    save_v_event_T save_v_event;
    dict_T *v_event = get_v_event(&save_v_event);
    if (data && data->type == kObjectTypeDict) {
      for (size_t i = 0; i < data->data.dict.size; i++) {
        KeyValuePair item = data->data.dict.items[i];
        typval_T tv;
        object_to_vim(item.value, &tv, &err);
        if (ERROR_SET(&err)) {
          api_clear_error(&err);
          continue;
        }
        tv_dict_add_tv(v_event, item.key.data, item.key.size, &tv);
      }
    }
    tv_dict_set_keys_readonly(v_event);

    apply_autocmds_group(event, fname, fname_io, false, group, buf, eap, data);
    restore_v_event(v_event, &save_v_event);
  }

  if (data) {
    api_free_object(*data);
    xfree(data);
  }
  xfree(e);
}

/// Execute autocommands for "event" and file name "fname".
///
/// @param event event that occurred
/// @param fname filename, NULL or empty means use actual file name
/// @param fname_io filename to use for <afile> on cmdline
/// @param force When true, ignore autocmd_busy

Review comment (suggestion):
```
= Direct leak of 8 byte(s) in 4 object(s) allocated from:
=     #0 0x56474fd71da3 in malloc (/home/runner/work/neovim/neovim/build/bin/nvim+0xd2dda3) (BuildId: 3ca905c7565ef1e6de420bec49eabad8f09550cb)
=     #1 0x564750a10c34 in try_malloc /home/runner/work/neovim/neovim/src/nvim/memory.c:113:15
=     #2 0x564750a10e94 in xmalloc /home/runner/work/neovim/neovim/src/nvim/memory.c:147:15
=     #3 0x564750a112aa in xmallocz /home/runner/work/neovim/neovim/src/nvim/memory.c:219:15
=     #4 0x564750a113e3 in xmemdupz /home/runner/work/neovim/neovim/src/nvim/memory.c:237:17
=     #5 0x5647502c7446 in decode_string /home/runner/work/neovim/neovim/src/nvim/eval/decode.c:278:69
=     #6 0x56474fe3c10d in object_to_vim_take_luaref /home/runner/work/neovim/neovim/src/nvim/api/private/converter.c:307:11
=     #7 0x56474fe3b298 in object_to_vim /home/runner/work/neovim/neovim/src/nvim/api/private/converter.c:268:3
```

Code after:

  multiqueue_put(deferred_events, deferred_event, evdata);
}

/// Executes a deferred autocommand event.
static void deferred_event(void **argv)
{
  AutoCmdEvent *e = argv[0];
  event_T event = e->event;
  char *fname = e->fname;
  char *fname_io = e->fname_io;
  int group = e->group;
  exarg_T *eap = e->eap;
  Object *data = e->data;

  Error err = ERROR_INIT;
  buf_T *buf = find_buffer_by_handle(e->buf, &err);
  if (buf) {
    // Copy `data` to `v:event`.
    save_v_event_T save_v_event;
    dict_T *v_event = get_v_event(&save_v_event);
    if (data && data->type == kObjectTypeDict) {
      for (size_t i = 0; i < data->data.dict.size; i++) {
        KeyValuePair item = data->data.dict.items[i];
        typval_T tv;
        object_to_vim(item.value, &tv, &err);
        if (ERROR_SET(&err)) {
          api_clear_error(&err);
          continue;
        }
        tv_dict_add_tv(v_event, item.key.data, item.key.size, &tv);
        tv_clear(&tv);
      }
    }
    tv_dict_set_keys_readonly(v_event);

    aco_save_T aco;
    aucmd_prepbuf(&aco, buf);
    apply_autocmds_group(event, fname, fname_io, false, group, buf, eap, data);
    aucmd_restbuf(&aco);

    restore_v_event(v_event, &save_v_event);
  }

  xfree(fname);
  xfree(fname_io);
  if (data) {
    api_free_object(*data);
    xfree(data);
  }
  xfree(e);