Skip to content

Request: Limit "Fields cannot be injected" to exact classes #3361

@idbrii

Description

@idbrii

How are you using the lua-language-server?

NeoVim

Which OS are you using?

Windows

What is the issue affecting?

Annotations

Expected Behaviour

The docs say:

Marking the class as (exact) means fields cannot be injected after the definition.

So when I use @class, I expect to not get any injection warnings unless I use (exact). I see this came up before in #2710, so I'd add: I should be able to get a warning for injections in exact classes while silencing warnings in non-exact classes.

Actual Behaviour

Any time I add a field outside of the constructor (and sometimes even ones that are defined in my class constructor), I get this warning:

Fields cannot be injected into the reference of ClassX for field_y. To do so, use ---@class for instance_z.

The @class annotation makes lua too strict. I'm using lua because I want to be able to inject fields.

Using annotations has multiple effects:

  1. It improves completion.
  2. It improves symbol lookups like goto definition.
  3. It does type checking.

But it's not great that adding annotations to get 1+2 results in tons of warnings from 3.

I understand that people may not want to add (exact) all over their codebase, but I don't want to add ---@field [any] any to all of mine.

I'd propose a change to diagnostic flags to allow independently controlling these two cases:

  • inject-field: suppress inject field diagnostic for non-exact classes.
  • inject-field-exact: suppress inject field diagnostic for exact classes.

Reproduction steps

Using neovim and installed luals with Mason. Version: pkg:github/luals/lua-language-server@3.17.1

-- Stripped-down version of https://github.com/rxi/classic/blob/master/classic.lua with an annotation added to be close to real-world class usage.
--
-- classic
--
-- Copyright (c) 2014, rxi
--
-- This module is free software; you can redistribute it and/or modify it under
-- the terms of the MIT license. See LICENSE for details.

--- @class Object
local Object = {}
Object.__index = Object

function Object:new()
end

function Object:extend()
  local cls = {}
  for k, v in pairs(self) do
    if k:find("__") == 1 then
      cls[k] = v
    end
  end
  cls.__index = cls
  cls.super = self
  setmetatable(cls, self)
  return cls
end

function Object:__call(...)
  local obj = setmetatable({}, self)
  obj:new(...)
  return obj
end

-- /end classic



--- @class Item : Object
local Item = Object:extend()

function Item:new(id, category)
	self.id = id
	self.category = category
	self.count = 0
end

function Item:Print()
	print("Item", self.id, self.category, self.count)
end

--- @param item Item
function FlagDebugItem(item)
	item:Print()  -- with the annotation, this completes. Yay!
	item.is_cheater = true  -- Warning :( "Fields cannot be injected into the reference of `Item` for `is_cheater`."
end

If I configure luals to add inject-field to the Lua.diagnostics.disable list, then the warning goes away. But the warning doesn't come back when I make Item an exact class with this annotation: --- @class (exact) Item : Object

Workaround: Add a fake field of any type:

--- @class Item : Object
--- @field [any] any
local Item = Object:extend()

Additional Notes

No response

Log File

[WARN][2026-02-23 11:41:41] ...lsp/handlers.lua:564	"More than 100000 files have been scanned. The current scanned directory is `D:\\code\\project\\br`. Please see the [FAQ](https://luals.github.io/wiki/faq/#how-can-i-improve-startup-speeds) to see how you can include fewer files. It is also possible that your [configuration is incorrect](https://luals.github.io/wiki/faq#why-is-the-server-scanning-the-wrong-folder)."
[WARN][2026-02-23 14:32:31] .../lua/vim/lsp.lua:52	"method textDocument/typeDefinition is not supported by any of the servers registered for the current buffer"
[START][2026-02-23 15:29:24] LSP logging initiated
[ERROR][2026-02-23 15:29:24] ...m/lsp/client.lua:620	"LSP[lua_ls]"	"Cannot find request with id 125 whilst attempting to cancel"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions