lua-language-server icon indicating copy to clipboard operation
lua-language-server copied to clipboard

enum error

Open xuhuanzy opened this issue 1 year ago • 3 comments

How are you using the lua-language-server?

Visual Studio Code Extension (sumneko.lua)

Which OS are you using?

Windows

What is the issue affecting?

Type Checking

Expected Behaviour

The notification indicates an invalid enumeration value.

Actual Behaviour

The enumeration variable is assigned incorrect values without any notification. image

Reproduction steps

---@enum NPBehaveNodeState
NPBehaveNodeState = {
    Inactive = "Inactive",
    Active = "Active",
    StopRequested = "StopRequested",
}

---@class Test
---@field CurrentState NPBehaveNodeState
local test = {}

test.CurrentState = NPBehaveNodeState.Active
-- no error prompt.
test.CurrentState = NPBehaveNodeState.Active123

Additional Notes

No response

Log File

No response

xuhuanzy avatar Jan 08 '24 09:01 xuhuanzy

related #2377

d-enk avatar Jan 09 '24 14:01 d-enk

I use enums generated at runtime and since there is no way to annotate a variable as @enum I generate additional @class like this:

---@diagnostic disable: unused-local
--luacheck: ignore

---@enum Status
local _ = {
	UNKNOWN = 0,
	NEW = 1,
	OLD = 2,
}

---@class Status.enum: {}
---@field UNKNOWN Status|Status.UNKNOWN: 0
---@field NEW Status|Status.NEW: 1
---@field OLD Status|Status.OLD: 2

---@return Status.enum
local function generate_enum()
	return {}
end

local Status = generate_enum()

---@type Status.OLD
local s_old = Status.OLD

---@type Status
local s = Status.UNKNOWN
s = Status.NEW
s = Status.OLD
s = Status.QWE -- Undefined field `QWE`

s = 0

I would also like = 0 to throw warning

d-enk avatar Jan 09 '24 15:01 d-enk

yes. Its kinda pointless to have enum if no errors are thrown when you use invalid values

i'm having to use a class instead

---@class sfxChar
---@field jump string
---@field get_item string

sfxChar = {
    jump = 'jump',
    get_item = 'get_item',
}

function lol()
    print(sfxChar.blaaaa); -- throws error
end

dganzella avatar Jan 27 '24 12:01 dganzella

Simpler example which also fails:

  ---@enum NPBehaveNodeState
  NPBehaveNodeState = {
    Inactive = 'Inactive',
    Active = 'Active',
    StopRequested = 'StopRequested',
  }

  ---@type NPBehaveNodeState
  local test

  -- this errors correctly
  test = 'Active123'

  -- this doesn't error, incorrect
  test = NPBehaveNodeState.Active123

Basically it only validates when passed a literal (string in this case)...

tmillr avatar Aug 11 '24 11:08 tmillr

duplicate of https://github.com/LuaLS/lua-language-server/issues/2384

And as I commented there:https://github.com/LuaLS/lua-language-server/issues/2384#issuecomment-2198052928, undefined-field diagnostics only check for typed tables (for performance reason I guess), but the enum binded table are not typed. If somehow the binded tables can be marked with some special flags, then maybe they can also be checked in the undefined-field diagnostics 😕


Currently my workaround is to also annotate the table with ---@class:

---@enum TestEnum.*
local TestEnum = {  ---@class TestEnum
    a = 1,
    b = 2,
    c = 3,
}

---@type TestEnum.*
local a = TestEnum.a    --< ok

---@type TestEnum.*
local d = TestEnum.d    --< undefined field

tomlau10 avatar Aug 12 '24 01:08 tomlau10

duplicate of #2384

And as I commented there:#2384 (comment), undefined-field diagnostics only check for typed tables (for performance reason I guess), but the enum binded table are not typed. If somehow the binded tables can be marked with some special flags, then maybe they can also be checked in the undefined-field diagnostics 😕

Currently my workaround is to also annotate the table with ---@class:

---@enum TestEnum.*
local TestEnum = {  ---@class TestEnum
    a = 1,
    b = 2,
    c = 3,
}

---@type TestEnum.*
local a = TestEnum.a    --< ok

---@type TestEnum.*
local d = TestEnum.d    --< undefined field

FUCK ME THIS WORKS THANSK

U R THE HERO OF THE DAY

gonna make a replace macro right now

dganzella avatar Aug 14 '24 16:08 dganzella

---@enum (.*)
(.*)\{

---@enum $1.*
$2{ ---@class $1

if anyone wants to replace

dganzella avatar Aug 14 '24 16:08 dganzella

Actually you don't need to follow my annotation practice exactly 😂 (I mean the TestEnum.*) You can keep you enum type name as TestEnum, then name the underlying class to something like _TestEnum. The key point is also annotate the table with ---@class (with whichever name you want)

Maybe that way you can modify less codes @dganzella


The following should also work

---@enum TestEnum
local TestEnum = {  ---@class _TestEnum
    a = 1,
    b = 2,
    c = 3,
}

---@type TestEnum
local a = TestEnum.a    --< ok

---@type TestEnum
local d = TestEnum.d    --< undefined field

tomlau10 avatar Aug 15 '24 02:08 tomlau10

Actually you don't need to follow my annotation practice exactly 😂 (I mean the TestEnum.*) You can keep you enum type name as TestEnum, then name the underlying class to something like _TestEnum. The key point is also annotate the table with ---@class (with whichever name you want)

Maybe that way you can modify less codes @dganzella


The following should also work

---@enum TestEnum
local TestEnum = {  ---@class _TestEnum
    a = 1,
    b = 2,
    c = 3,
}

---@type TestEnum
local a = TestEnum.a    --< ok

---@type TestEnum
local d = TestEnum.d    --< undefined field

haha yes I noticed that and changed the replace macro to add the .* to the class annotation instead of the enum

I did like the .*

Anyway, I was INSTANTLY able to spot two snaeaky issues in my code. Thank you

dganzella avatar Aug 15 '24 03:08 dganzella

@sumneko please fix

xuhuanzy avatar Aug 15 '24 03:08 xuhuanzy

@sumneko please fix

@sumneko is quite the character

  • makes an amazing tool with just a simple missing feature
  • refuses to elaborate
  • leaves

gigachad

dganzella avatar Aug 15 '24 03:08 dganzella

Like many of us, the author has a full time job. 😅 The amazing thing about open source projects is that contributions are welcome, and maybe we can fix something that we desperately want on our own. 👀

tomlau10 avatar Aug 15 '24 03:08 tomlau10

Like many of us, the author has a full time job. 😅 The amazing thing about open source projects is that contributions are welcome, and maybe we can fix something that we desperately want on our own. 👀

👀 The amazing thing is that we would need to learn the entire thing just to make something that clearly can be easily done by him since the behaviour is present in other annotations. It's not a different feature or some kind of extension.

Of course I know he is not obliged to do anything, I'm not retarded, the funny thing is just to run a marathon just to give up 2 meters from the goal. And its just a fun remark, not an actual complaint.

dganzella avatar Aug 15 '24 05:08 dganzella

Temporary solution: Modify C:\Users\<UserName>\.vscode\extensions\sumneko.lua-3.10.3-win32-x64\server

Index: script/vm/compiler.lua
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
--- a/script/vm/compiler.lua	(revision 2fe2ff371a8d2ee97a46c1b48eb421d9f8ee65ad)
+++ b/script/vm/compiler.lua	(date 1723768685862)
@@ -73,6 +73,24 @@
     return false
 end
 
+-- 该函数有副作用,会给source绑定node!
+---@param source parser.object
+---@return boolean
+function vm.bindDocsEnum(source)
+    local docs = source.value.bindDocs
+    if not docs then
+        return false
+    end
+    for i = #docs, 1, -1 do
+        local doc = docs[i]
+        if doc.type == 'doc.enum' then
+            vm.setNode(source, vm.compileNode(doc))
+            return true
+        end
+    end
+    return false
+end
+
 ---@param source parser.object | vm.variable
 ---@param key string|vm.global|vm.ANY
 ---@param pushResult fun(res: parser.object, markDoc?: boolean)
@@ -1105,6 +1123,8 @@
     local hasMarkDoc
     if source.bindDocs then
         hasMarkDoc = vm.bindDocs(source)
+    elseif source.value and source.value.bindDocs then
+        hasMarkDoc = vm.bindDocsEnum(source)
     end
     local hasMarkParam
     if not hasMarkDoc then

The structure of Enum is not consistent with that of Class, but the error from Class was borrowed here, so this solution is temporary.

@sumneko can pr?

xuhuanzy avatar Aug 16 '24 00:08 xuhuanzy

I tested the patch and it works (can throw undefined error) 🎉 . But seems there is a side effect:

  • when hover preview over the table, the content is displayed twice image

I guess it's because now the enum type is injected into this table (through the vm.setNode in vm.bindDocsEnum) 🤔

tomlau10 avatar Aug 16 '24 01:08 tomlau10

I will look into it today

sumneko avatar Aug 16 '24 06:08 sumneko

enum 的问题有点复杂,见下面这个例子:

---@enum A
local t = {
    x = 1,
    y = 2,
}

---@param x A
local function f(x) end

从下面的用法来看,A 其实等价于 ---@alias A 1|2,问题是 local t 是什么? 目前的实现里,t 什么都不是,直接没有和任何 doc 进行绑定,这就是为什么使用 t.z 不会出现警告。

如果一定要给 t 一个类型,那么应该是 { [any]: A } ,因此你不能将 A 直接绑定给 t

我目前的想法是生成一个特殊命名的class给 t

sumneko avatar Aug 16 '24 10:08 sumneko

改的时候就发现了, enum并不是独立的类型, 我觉得给enum类型加个独有字段用于诊断未定义的值最简单

xuhuanzy avatar Aug 16 '24 10:08 xuhuanzy

@sumneko (partial) 失效

xuhuanzy avatar Aug 17 '24 11:08 xuhuanzy

The following 2 related/duplicated issues can also be closed:

  • https://github.com/LuaLS/lua-language-server/issues/1750
  • https://github.com/LuaLS/lua-language-server/issues/2384

tomlau10 avatar Aug 18 '24 02:08 tomlau10