mrcjkb / rustaceanvim

Supercharge your Rust experience in Neovim! A heavily modified fork of rust-tools.nvim
GNU General Public License v2.0
1.28k stars 47 forks source link

Neotest adapter throws `attempt to index local 'tree' (a nil value)` upon first invocation #399

Closed antoineco closed 1 month ago

antoineco commented 1 month ago

Have you read the docs and searched existing issues?

Neovim version (nvim -v)

v0.10.0

Operating system/version

Debian 12 (bookworm)

Output of :checkhealth rustaceanvim

# :checkhealth rustaceanvim
rustaceanvim: require("rustaceanvim.health").check()

Checking for Lua dependencies
- WARNING dap not installed. Needed for debugging features [mfussenegger/nvim-dap](https://github.com/mfussenegger/nvim-dap)

Checking external dependencies
- OK rust-analyzer: found rust-analyzer 0.3.1958-standalone
- OK Cargo: found cargo 1.78.0 (54d8815d0 2024-03-26)
- OK rustc: found rustc 1.78.0 (9b00956e5 2024-04-29)

Checking config
- OK No errors found in config.

Checking for conflicting plugins
- OK No conflicting plugins detected.

Checking for tree-sitter parser
- OK tree-sitter parser for Rust detected.

How to reproduce the issue

[!NOTE] This can be reproduced both on v4 and master .

Open the following Rust file using the minimal configuration below:

fn main() {}

#[test]
fn test() {}

Type ,tt to run the nearest test, and observe the issue.

Expected behaviour

The test runs without throwing an error.

Actual behaviour

The following error is thrown:

Error executing vim.schedule lua callback: ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:100: Async task failed without callback: The coroutine failed with this message:
...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:100: Async task failed without callback: The coroutine failed with this message:
...vim/lazy/neotest/lua/neotest/consumers/state/tracker.lua:47: attempt to index local 'tree' (a nil value)
stack traceback:
        ...vim/lazy/neotest/lua/neotest/consumers/state/tracker.lua: in function 'is_test'
        ...vim/lazy/neotest/lua/neotest/consumers/state/tracker.lua:155: in function 'update_running'
        ...e/nvim/lazy/neotest/lua/neotest/consumers/state/init.lua:49: in function 'listener'
        ...are/nvim/lazy/neotest/lua/neotest/client/events/init.lua:51: in function <...are/nvim/lazy/neotest/lua/neotest/client/events/init.lua:47>
stack traceback:
        [C]: in function 'error'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:100: in function 'close_task'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:122: in function 'step'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:150: in function 'run'
        ...are/nvim/lazy/neotest/lua/neotest/client/events/init.lua:47: in function 'emit'
        ...hare/nvim/lazy/neotest/lua/neotest/client/state/init.lua:95: in function 'update_running'
        ...ocal/share/nvim/lazy/neotest/lua/neotest/client/init.lua:82: in function 'run_tree'
        ...al/share/nvim/lazy/neotest/lua/neotest/consumers/run.lua:73: in function 'func'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:173: in function <...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:172>
stack traceback:
        [C]: in function 'error'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:100: in function 'close_task'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:122: in function 'cb'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:188: in function 'waiter'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/control.lua:57: in function 'set'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/init.lua:109: in function 'cb'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:105: in function 'close_task'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:127: in function 'cb'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/tasks.lua:188: in function 'waiter'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/control.lua:116: in function 'wake'
        ...user/.local/share/nvim/lazy/nvim-nio/lua/nio/control.lua:129: in function 'set'
        ...nvim/lazy/rustaceanvim/lua/rustaceanvim/neotest/init.lua:78: in function 'handler'
        /usr/local/share/nvim/runtime/lua/vim/lsp/client.lua:685: in function ''
        vim/_editor.lua: in function <vim/_editor.lua:0>

The minimal config used to reproduce this issue.

-- Minimal nvim config with lazy
-- Assumes a directory in $NVIM_DATA_MINIMAL
-- Start with
--
-- export NVIM_DATA_MINIMAL=$(mktemp -d)
-- export NVIM_APP_NAME="nvim-ht-minimal"
-- nvim -u NORC -u minimal.lua
--
-- Then exit out of neovim and start again.

vim.g.mapleader = ","

-- Ignore default config
local config_path = vim.fn.stdpath('config')
vim.opt.rtp:remove(config_path)

-- Ignore default plugins
local data_path = vim.fn.stdpath('data')
local pack_path = data_path .. '/site'
vim.opt.packpath:remove(pack_path)

-- bootstrap lazy.nvim
data_path = assert(os.getenv('NVIM_DATA_MINIMAL'), '$NVIM_DATA_MINIMAL environment variable not set!')
local lazypath = data_path .. '/lazy/lazy.nvim'
local uv = vim.uv
  ---@diagnostic disable-next-line: deprecated
  or vim.loop
if not uv.fs_stat(lazypath) then
  vim.fn.system {
    'git',
    'clone',
    '--filter=blob:none',
    'git@github.com:folke/lazy.nvim.git',
    '--branch=stable',
    lazypath,
  }
end
vim.opt.rtp:prepend(lazypath)

local lazy = require('lazy')

lazy.setup({
  {
    'mrcjkb/rustaceanvim',
    --version = '^4',
    init = function()
      -- Configure rustaceanvim here
      vim.g.rustaceanvim = {}
    end,
    lazy = false,
  },
  -- Add any other plugins needed to reproduce the issue.
  -- see https://github.com/folke/lazy.nvim#-lazynvim for details.
  {
    'nvim-neotest/neotest',
    dependencies = {
      'nvim-lua/plenary.nvim',
      'nvim-neotest/nvim-nio',
      'antoinemadec/FixCursorHold.nvim',
      -- adapters
      {
        'fredrikaverpil/neotest-golang'
      }
    },
    config = function()
      require 'neotest'.setup {
        adapters = {
          require 'neotest-golang',
          require 'rustaceanvim.neotest'
        }
      }
    end,
    keys = {
      {
        "<leader>tt",
        function()
          require "neotest".run.run()
        end,
        desc = "Run Nearest Test"
      }
    }
  }
}, { root = data_path, state = data_path .. '/lazy-state.json', lockfile = data_path .. '/lazy-lock.json' })

Additional notes

(keeping this section up to date with the comments)

  1. If I disable the neotest-golang adapter by commenting require "neotest-golang", I can no longer reproduce the issue. neotest-golang itself exhibits no problem at all. I can open Go tests and run them without causing an error. Likewise, I can run Rust tests without error when neotest-golang is out of the picture, but not when the two are enabled.

  2. I cannot reproduce the issue with another adapter than neotest-golang, such as neotest-plenary or neotest-python. The issue seems to only materialize when neotest-golang is included in the adapters table.

  3. The error occurs only on the first test run. If I retry, the tests run without error.

  4. I cannot reproduce the issue if I run the tests before rust-analyzer has finished indexing the project. But if I run tests after rust-analyzer has finished indexing ([Indexing] (100%) received through LspProgress events), the issue occurs consistently in the conditions explained above.

mrcjkb commented 1 month ago

Hey 👋

Thanks for the detailed report. ~I'm a bit confused. The warning note says you couldn't reproduce this with the minimal config, but you ticked off the checkbox saying you can?~

This could also be a neotest issue. Are you getting the same error with any other adapters?

I see you've added neotest-golang to the minimal config. Does it not occur without it?

(Note: I'm traveling this weekend, so I probably won't get back to this before Tuesday)

antoineco commented 1 month ago

I think I managed to narrow the issue down and updated the description.

If I disable the neotest-golang adapter by commenting require "neotest-golang", I can no longer reproduce the issue. What is confusing to me is that neotest-golang itself exhibits no problem at all. I can open Go tests and run them without causing an error. Likewise, I can run Rust tests without error when neotest-golang is out of the picture, but not when the two are enabled.

Additionally, the error occurs only on the first test run. If I retry, the tests run without error.

mrcjkb commented 1 month ago

Hmm, that's very curious indeed. It looks like the error is thrown here.

I'm not sure, but this looks like a neotest issue. Does it happen with any other combination of adapters?

E.g.

Or is it just neotest-go combined with rustaceanvim?

antoineco commented 1 month ago

I haven't tried any other combination of adapters yet, but will do and report here ASAP. Enjoy your vacation! 🙌

antoineco commented 1 month ago

I observed something quite curious which I think is worth adding.

I cannot reproduce the issue if I run the tests before rust-analyzer has finished indexing the project. But if I run tests after rust-analyzer is done indexing ([Indexing] (100%) received through LspProgress events), the issue occurs consistently in the conditions explained in the description.

fredrikaverpil commented 1 month ago

Author of neotest-golang here 😄 did you get around to check if this also happens if you e.g. switch out one of the adapters for another one?

I'm using the neotest-golang and neotest-plenary side-by-side in the neotest-golang repo and I'm not seeing any issues:

image

You can try it out here in the neotest-golang repo if you wish, to see if you get the same issues there. Here's my lua setup: https://github.com/fredrikaverpil/dotfiles/blob/03e93510aa5897b35b5729f17e3dcc323d80e82d/nvim-fredrik/lua/lang/lua.lua

mrcjkb commented 1 month ago

did you get around to check if this also happens if you e.g. switch out one of the adapters for another one?

Same here. I use rustaceanvim together with neotest... -haskell, -busted, and -java, and haven't faced any issues.

I have yet to find time to reproduce this with -golang.

I cannot reproduce the issue if I run the tests before rust-analyzer has finished indexing the project.

This makes me think that it has to be rustaceanvim in combination with another adapter. @antoineco is your example from a cargo project or a standalone file? If the former, could you please provide a project or steps to create the project (including any other files and the cwd).

antoineco commented 1 month ago

is your example from a cargo project or a standalone file? If the former, could you please provide a project or steps to create the project (including any other files and the cwd).

I noticed the issue while working on this code specifically: https://github.com/antoineco/adventofcode2023

antoineco commented 1 month ago

@fredrikaverpil I tried with neotest-plenary and neotest-golang like you suggested.

I don't get the error when neotest-plenary is loaded (included in the adapters table), regardless of the timing explained in https://github.com/mrcjkb/rustaceanvim/issues/399#issuecomment-2118680313. But I do as soon as I uncomment neotest-golang in the configuration below:

  {
    "nvim-neotest/neotest",
    dependencies = {
      "nvim-neotest/nvim-nio",
      "antoinemadec/FixCursorHold.nvim",
      -- adapters
      {
        "nvim-neotest/neotest-plenary",
        "fredrikaverpil/neotest-golang"
      }
    },
    config = function()
      require "neotest".setup {
        adapters = {
          require "neotest-plenary",
          --require "neotest-golang",
          require "rustaceanvim.neotest"
        }
      }
    end
  }

I updated the additional notes in the issue description with this new information.

fredrikaverpil commented 1 month ago

I just tried running lua/go/rust tests in the neotest-golang repo, and I can't see any issues on my end. I know it doesn't hold a lot of tests so it's nearly a minimal reproducible example of that you should be able to combine multiple adapters like this, without issues.

https://github.com/mrcjkb/rustaceanvim/assets/994357/bf7c6600-0946-4b10-9b9c-05f91be9f659

I took a quick look at the stacktrace you posted. In the Neotest StateTracker.update_running method, there's a call to the StateTracker.is_test method, which is crashing because the tree variable is unexpectedly nil. I haven't really read all the code and I'm just guessing now, but I would assume this code is related to updating the state of the tests (like if they are running, if they passed, failed or were skipped). For some reason, on your end, it looks like there is a problem with updating one or more tests due to some unknown reason.

I would probably start out with adding in some poor man's debug print statements (like print(vim.inspect(tree)) into the update_running method (on macOS it's something like ~/.local/share/neovim/lazy/neotest/lua/neotest/consumers/state/tracker.lua). It would for example be interesting to print the name of the test being updated and the value of tree. I would also disable other plugins just to see if something else is interfering, perhaps interfering with the AST parsing (seems odd though).

If you still can't find anything, it would be helpful if you could set up a new GitHub repo where you can consistently reproduce the issue.

antoineco commented 1 month ago

@fredrikaverpil I cannot reproduce the issue either using your method of opening the Neotest summary and running the tests from there. However, this is not how the issue manifests itself to me.

In the case described here, the problem occurs:

mrcjkb commented 1 month ago

:thinking:

I can't reproduce this if I add the adapters in this order:

{
    require('rustaceanvim.neotest'),
    require('neotest-golang'),
}

but I can if I add them in this order:

{
    require('neotest-golang'),
    require('rustaceanvim.neotest'),
}
mrcjkb commented 1 month ago

@rcarriga maybe you have some insights?

It looks like it's something related to task management in nvim-nio. I used nio.control.future() to wait for the experimental/runnables LSP response, and the error is thrown when calling future.set(runnables).

I've refactored that to use nio.lsp.Client, but it still results in the same error if neotest-golang is added before rustaceanvim.neotest.

rcarriga commented 1 month ago

Just having a look, I think this is a race condition issue from when events are processed by the state consumer. For context, the state consumer is basically just providing a way to interact with the state of neotest without having to run async code.

It assumes that events are processed sequentially but there is really no guarantee to that. I've put in a very simple change in a branch fix/consumers-state/wrap-listeners that might address the issue, but I'll need @antoineco to test if you could? If that doesn't work, it'll likely be a larger fix.

Alternatively you can just disable the consumer by passing

    state = {
      enabled = false
    },

in your neotest config

antoineco commented 1 month ago

@rcarriga I just checked out your fix/consumers-state/wrap-listeners branch and replicated the same procedure as described in this issue, multiple times just to be sure.

Out of my ~10 attempts the issue didn't materialize a single time. I'm fine considering the issue resolved. Thank you very much! 🙌