summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelog.md8
-rw-r--r--script/core/diagnostics/assign-type-mismatch.lua26
-rw-r--r--script/vm/node.lua2
-rw-r--r--test/diagnostics/type-check.lua33
4 files changed, 68 insertions, 1 deletions
diff --git a/changelog.md b/changelog.md
index 4e457622..4c3a4040 100644
--- a/changelog.md
+++ b/changelog.md
@@ -3,6 +3,14 @@
## 3.4.1
* `NEW` settings:
* `type.weakNilCheck`
+* `CHG` allow type contravariance for `setmetatable` when initializing a class
+ ```lua
+ ---@class A
+ local a = {}
+
+ ---@class B: A
+ local b = setmetatable({}, { __index = a }) -- OK!
+ ```
* `FIX` [#1256](https://github.com/sumneko/lua-language-server/issues/1256)
* `FIX` [#1257](https://github.com/sumneko/lua-language-server/issues/1257)
* `FIX` [#1267](https://github.com/sumneko/lua-language-server/issues/1267)
diff --git a/script/core/diagnostics/assign-type-mismatch.lua b/script/core/diagnostics/assign-type-mismatch.lua
index a3d63ca8..d98aa021 100644
--- a/script/core/diagnostics/assign-type-mismatch.lua
+++ b/script/core/diagnostics/assign-type-mismatch.lua
@@ -30,6 +30,20 @@ local function hasMarkType(source)
return false
end
+---@param source parser.object
+---@return boolean
+local function hasMarkClass(source)
+ if not source.bindDocs then
+ return false
+ end
+ for _, doc in ipairs(source.bindDocs) do
+ if doc.type == 'doc.class' then
+ return true
+ end
+ end
+ return false
+end
+
---@async
return function (uri, callback)
local state = files.getState(uri)
@@ -72,6 +86,18 @@ return function (uri, callback)
if vm.canCastType(uri, varNode, valueNode) then
return
end
+
+ if value.type == 'select'
+ and value.sindex == 1
+ and value.vararg
+ and value.vararg.type == 'call'
+ and value.vararg.node.special == 'setmetatable'
+ and hasMarkClass(source) then
+ if vm.canCastType(uri, valueNode:copy():remove 'table', varNode) then
+ return
+ end
+ end
+
callback {
start = source.start,
finish = source.finish,
diff --git a/script/vm/node.lua b/script/vm/node.lua
index f0d0b0ba..5d8bcd75 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -227,7 +227,7 @@ function mt:remove(name)
or (c.type == 'doc.type.boolean' and name == 'false' and c[1] == false)
or (c.type == 'doc.type.table' and name == 'table')
or (c.type == 'doc.type.array' and name == 'table')
- or (c.type == 'doc.type.sign' and name == 'table')
+ or (c.type == 'doc.type.sign' and name == 'table')
or (c.type == 'doc.type.function' and name == 'function') then
table.remove(self, index)
self[c] = nil
diff --git a/test/diagnostics/type-check.lua b/test/diagnostics/type-check.lua
index 32f60f82..f1f14b48 100644
--- a/test/diagnostics/type-check.lua
+++ b/test/diagnostics/type-check.lua
@@ -631,5 +631,38 @@ n = nb
]]
config.set(nil, 'Lua.type.weakNilCheck', false)
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local <!b!> = a
+]]
+
+TEST [[
+---@class A
+local a = {}
+a.__index = a
+
+---@class B: A
+local b = setmetatable({}, a)
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local b = setmetatable({}, {__index = a})
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B
+local <!b!> = setmetatable({}, {__index = a})
+]]
+
config.remove(nil, 'Lua.diagnostics.disable', 'unused-local')
config.remove(nil, 'Lua.diagnostics.disable', 'undefined-global')