NeoVim + LSP shows diagnostic for external crates

I'm using NeoVim + LSP Zero plugin with Rust Analyzer.

If I just create new crate via cargo init my_program, then just open it via nvim my_program and try to go to the definition of println in std, LSP diagnostic starts to show me diagnostic from std, tons of this:

/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|100 col 1-32 error| `#![feature]` may not be used on the stable release channel
/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|100 col 12-30 error| `#![feature]` may not be used on the stable release channel
/home/user/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/lib.rs|101 col 12-30 error| `#![feature]` may not be used on the stable release channel

Same happens if I go to definitions in other external crates.
I also tried to use CoC instead of native LSP, but got exactly the same result.

On the other hand, if I try to use Helix editor or even VSCode, there is no such behavior.

What is correct way to disable diagnostic for crates outside your workspace in NeoVim?

I am also having this issue.

When opening a file by following go to definition you can see that the external crates gets added as a workspace folder

print(vim.inspect(vim.lsp.buf.list_workspace_folders()))

Have you found a fix for it?

I'm using rust-tool.vim and ChatGpt cooked up this solution for me based on telling RA that the root_dir can only be the current workspace. Should be portable as long as the lsp is rust-analyzer.

First define these helpers

local function ensure_uri_scheme(uri)
    if not vim.startswith(uri, "file://") then
        return "file://" .. uri
    end
    return uri
end

local function is_in_workspace(uri)
    uri = ensure_uri_scheme(uri)
    local path = vim.uri_to_fname(uri)
    local workspace_dir = vim.fn.getcwd()

    return vim.startswith(path, workspace_dir)
end

local function filter_diagnostics(diagnostics)
    local filtered_diagnostics = {}
    for _, diagnostic in ipairs(diagnostics) do
        if is_in_workspace(diagnostic.source) then
            table.insert(filtered_diagnostics, diagnostic)
        end
    end
    return filtered_diagnostics
end

Then configure the root_dir option in lspconfig or lsp zero. In my case I just used rust-tools config directly and it worked.

local lspconfig = require('lspconfig')
let rt = require('rust-tools')
rt.setup({
    --....
    --....
    server = {
       root_dir = function(startpath)
            local startpath_uri = vim.uri_from_fname(startpath)
            if not is_in_workspace(startpath) then
                return nil
            end

            return lspconfig.util.root_pattern("Cargo.toml", "rust-project.json")(startpath)
      end,
    }
})

It also gave me another option I can set with a function that does diagnostic filtering based on if the file is from the workspace. But I'm satisfied with RA being disabled all together with the above solution. Here is the filtering snippet

local lspconfig = require('lspconfig')
let rt = require('rust-tools')
rt.setup({
     --....
     --....
     server = {
          handlers = {
            ["textDocument/publishDiagnostics"] = function(err, method, result, client_id, bufnr, config)
            if not result or not result.uri then
                return
            end

            local uri = result.uri
            local bufnr = vim.uri_to_bufnr(uri)

            if not bufnr then
                return
            end

            if is_in_workspace(uri) then
                local diagnostics = vim.lsp.diagnostic.to_severity(result.diagnostics)
                diagnostics = filter_diagnostics(diagnostics)
                vim.lsp.diagnostic.save(diagnostics, bufnr, client_id)
                if vim.api.nvim_buf_is_loaded(bufnr) then
                    vim.lsp.diagnostic.set_signs(diagnostics, bufnr, client_id)
                end
            end
        end;
    },
}
})

This is how I solved the issue:

    local util = require('lspconfig.util')

    local rustPorjectDir = nil
    local function rustRootPattern(fname)
        local cargo_crate_dir = util.root_pattern 'Cargo.toml' (fname)
        local cmd = { 'cargo', 'metadata', '--no-deps', '--format-version', '1' }
        if cargo_crate_dir ~= nil then
            cmd[#cmd + 1] = '--manifest-path'
            cmd[#cmd + 1] = util.path.join(cargo_crate_dir, 'Cargo.toml')
        end
        local cargo_metadata = ''
        local cargo_metadata_err = ''
        local cm = vim.fn.jobstart(cmd, {
            on_stdout = function(_, d, _)
                cargo_metadata = table.concat(d, '\n')
            end,
            on_stderr = function(_, d, _)
                cargo_metadata_err = table.concat(d, '\n')
            end,
            stdout_buffered = true,
            stderr_buffered = true,
        })
        if cm > 0 then
            cm = vim.fn.jobwait({ cm })[1]
        else
            cm = -1
        end
        local cargo_workspace_dir = nil
        if cm == 0 then
            cargo_workspace_dir = vim.json.decode(cargo_metadata)['workspace_root']
            if cargo_workspace_dir ~= nil then
                cargo_workspace_dir = util.path.sanitize(cargo_workspace_dir)
            end
        else
            vim.notify(
                string.format('[lspconfig] cmd (%q) failed:\n%s', table.concat(cmd, ' '), cargo_metadata_err),
                vim.log.levels.WARN
            )
        end
        return cargo_workspace_dir
            or cargo_crate_dir
            or util.root_pattern 'rust-project.json' (fname)
            or util.find_git_ancestor(fname)
        -- return cargo_crate_dir
        --     or util.root_pattern 'rust-project.json' (fname)
        --     or util.find_git_ancestor(fname)
    end

    local function rust_root_dir(fname)
        local function get()
            rustPorjectDir = rustRootPattern(fname)
            vim.notify("Directory: " .. rustPorjectDir, "info", { title = "Rust project opened" })
            return rustPorjectDir
        end

        return rustPorjectDir or get()
    end

    lsp.configure("rust_analyzer", {
        root_dir = rust_root_dir,
    })

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.