Skip to content

A metatable __index function overrides a declared ---@type on main (Neovim's vim.env is "always falsy") #1319

Open

Description

@mareklangiewicz

On current main (56267ead, emmylua_check 0.30.0), reading a field of a table declared
---@type table<string, string?> produces the type inferred from its metatable's __index function instead
of the declared value type. That inference can also be wrong. Together they make every
if vim.env.X then in Neovim code an unnecessary-if ("always falsy"), because Neovim's runtime declares
vim.env exactly this way (runtime/lua/vim/_core/options.lua). 0.25.1 reports nothing.

def.lua:

G = {}

--- @type table<string, string?>
G.envA = setmetatable({}, {
  __index = function(_, k)
    local v = os.getenv(k)
    if v == nil then return nil end
    return v
  end,
})

--- @type table<string, string?>
G.envD = setmetatable({}, { __index = function(_, k) return 'always' end })

use.lua:

if G.envA.X then print 'A' end  -- main: Impossible `if` statement: this condition is always falsy
if G.envD.X then print 'D' end  -- main: Unnecessary `if` statement: this condition is always truthy

Expected: both are string? (the declared type), so neither if is reported. That is what 0.25.1 does.

Two separate problems seem to be involved:

  1. The declared type loses. envD is declared string?, but reads as string (always truthy). An
    explicit ---@type on the table should take precedence over what the metatable's __index returns.
  2. The __index return is inferred as nil. envA's function returns nil on one path and v
    (narrowed to string) on the other, which should be string?. It comes out as nil. With
    return os.getenv(k) the same table reads as string? and nothing is reported.

Controls: setmetatable({}, {}) with no __index, and a plain {}, both keep the declared string?.

Not bisected: 0.25.1 → main crosses the semantic_db rewrite.


Written with help from Claude Code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions