Skip to content

Commit 896a1af

Browse files
committed
infer definitions and types across chain exp
resolve #1561
1 parent e191247 commit 896a1af

File tree

5 files changed

+129
-124
lines changed

5 files changed

+129
-124
lines changed

changelog.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ server will generate `doc.json` and `doc.md` in `LOGPATH`.
1717
}
1818
```
1919
* `CHG` [#1177] re-support for symlinks, users need to maintain the correctness of symlinks themselves
20+
* `CHG` [#1561] infer definitions and types across chain expression
21+
```lua
22+
---@class myClass
23+
local myClass = {}
24+
25+
myClass.a.b.c.e.f.g = 1
26+
27+
---@type myClass
28+
local class
29+
30+
print(class.a.b.c.e.f.g) --> infered as integer
31+
```
2032
* `FIX` [#1567]
2133
* `FIX` [#1593]
2234
* `FIX` [#1606]
@@ -26,6 +38,7 @@ server will generate `doc.json` and `doc.md` in `LOGPATH`.
2638
[#1458]: https://github.com/sumneko/lua-language-server/issues/1458
2739
[#1557]: https://github.com/sumneko/lua-language-server/issues/1557
2840
[#1558]: https://github.com/sumneko/lua-language-server/issues/1558
41+
[#1561]: https://github.com/sumneko/lua-language-server/issues/1561
2942
[#1567]: https://github.com/sumneko/lua-language-server/issues/1567
3043
[#1593]: https://github.com/sumneko/lua-language-server/issues/1593
3144
[#1606]: https://github.com/sumneko/lua-language-server/issues/1606

script/vm/compiler.lua

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,84 @@ local function compileByGlobal(source)
18181818
end
18191819
end
18201820

1821+
local nodeSwitch;nodeSwitch = util.switch()
1822+
: case 'field'
1823+
: case 'method'
1824+
: call(function (source, lastKey, pushResult)
1825+
return nodeSwitch(source.parent.type, source.parent, lastKey, pushResult)
1826+
end)
1827+
: case 'getfield'
1828+
: case 'setfield'
1829+
: case 'getmethod'
1830+
: case 'setmethod'
1831+
: case 'getindex'
1832+
: case 'setindex'
1833+
: call(function (source, lastKey, pushResult)
1834+
local parentNode = vm.compileNode(source.node)
1835+
local uri = guide.getUri(source)
1836+
local key = guide.getKeyName(source)
1837+
if type(key) ~= 'string' then
1838+
return
1839+
end
1840+
if lastKey then
1841+
key = key .. vm.ID_SPLITE .. lastKey
1842+
end
1843+
for pn in parentNode:eachObject() do
1844+
searchFieldSwitch(pn.type, uri, pn, key, false, pushResult)
1845+
end
1846+
return key, source.node
1847+
end)
1848+
: case 'tableindex'
1849+
: case 'tablefield'
1850+
: call(function (source, lastKey, pushResult)
1851+
if lastKey then
1852+
return
1853+
end
1854+
local key = guide.getKeyName(source)
1855+
if type(key) ~= 'string' then
1856+
return
1857+
end
1858+
local uri = guide.getUri(source)
1859+
local parentNode = vm.compileNode(source.node)
1860+
for pn in parentNode:eachObject() do
1861+
searchFieldSwitch(pn.type, uri, pn, key, false, pushResult)
1862+
end
1863+
end)
1864+
: case 'doc.see.field'
1865+
: call(function (source, lastKey, pushResult)
1866+
if lastKey then
1867+
return
1868+
end
1869+
local parentNode = vm.compileNode(source.parent.name)
1870+
local uri = guide.getUri(source)
1871+
for pn in parentNode:eachObject() do
1872+
searchFieldSwitch(pn.type, uri, pn, source[1], false, pushResult)
1873+
end
1874+
end)
1875+
1876+
function vm.compileByNodeChain(source, pushResult)
1877+
local lastKey
1878+
local src = source
1879+
while true do
1880+
local key, node = nodeSwitch(src.type, src, lastKey, pushResult)
1881+
if not key then
1882+
break
1883+
end
1884+
src = node
1885+
lastKey = key
1886+
end
1887+
end
1888+
1889+
---@param source vm.object
1890+
local function compileByParentNode(source)
1891+
if not vm.getNode(source):isEmpty() then
1892+
return
1893+
end
1894+
vm.compileByNodeChain(source, function (result)
1895+
vm.setNode(source, vm.compileNode(result))
1896+
end)
1897+
end
1898+
18211899
---@param source vm.object
18221900
---@return vm.node
18231901
function vm.compileNode(source)
@@ -1846,6 +1924,7 @@ function vm.compileNode(source)
18461924
LOCK[source] = true
18471925
compileByGlobal(source)
18481926
compileByNode(source)
1927+
compileByParentNode(source)
18491928
matchCall(source)
18501929
LOCK[source] = nil
18511930

script/vm/def.lua

Lines changed: 1 addition & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -20,110 +20,6 @@ simpleSwitch = util.switch()
2020
end
2121
end)
2222

23-
local searchFieldSwitch = util.switch()
24-
: case 'table'
25-
: call(function (suri, obj, key, pushResult)
26-
for _, field in ipairs(obj) do
27-
if field.type == 'tablefield'
28-
or field.type == 'tableindex' then
29-
if guide.getKeyName(field) == key then
30-
pushResult(field)
31-
end
32-
end
33-
end
34-
end)
35-
: case 'global'
36-
---@param obj vm.global
37-
---@param key string
38-
: call(function (suri, obj, key, pushResult)
39-
if obj.cate == 'variable' then
40-
local newGlobal = vm.getGlobal('variable', obj.name, key)
41-
if newGlobal then
42-
for _, set in ipairs(newGlobal:getSets(suri)) do
43-
pushResult(set)
44-
end
45-
end
46-
end
47-
if obj.cate == 'type' then
48-
vm.getClassFields(suri, obj, key, false, pushResult)
49-
end
50-
end)
51-
: case 'local'
52-
: call(function (suri, obj, key, pushResult)
53-
local sources = vm.getLocalSourcesSets(obj, key)
54-
if sources then
55-
for _, src in ipairs(sources) do
56-
pushResult(src)
57-
end
58-
end
59-
end)
60-
: case 'doc.type.table'
61-
: call(function (suri, obj, key, pushResult)
62-
for _, field in ipairs(obj.fields) do
63-
local fieldKey = field.name
64-
if fieldKey.type == 'doc.field.name' then
65-
if fieldKey[1] == key then
66-
pushResult(field)
67-
end
68-
end
69-
end
70-
end)
71-
72-
local nodeSwitch;nodeSwitch = util.switch()
73-
: case 'field'
74-
: case 'method'
75-
: call(function (source, lastKey, pushResult)
76-
return nodeSwitch(source.parent.type, source.parent, lastKey, pushResult)
77-
end)
78-
: case 'getfield'
79-
: case 'setfield'
80-
: case 'getmethod'
81-
: case 'setmethod'
82-
: case 'getindex'
83-
: case 'setindex'
84-
: call(function (source, lastKey, pushResult)
85-
local parentNode = vm.compileNode(source.node)
86-
local uri = guide.getUri(source)
87-
local key = guide.getKeyName(source)
88-
if type(key) ~= 'string' then
89-
return
90-
end
91-
if lastKey then
92-
key = key .. vm.ID_SPLITE .. lastKey
93-
end
94-
for pn in parentNode:eachObject() do
95-
searchFieldSwitch(pn.type, uri, pn, key, pushResult)
96-
end
97-
return key, source.node
98-
end)
99-
: case 'tableindex'
100-
: case 'tablefield'
101-
: call(function (source, lastKey, pushResult)
102-
if lastKey then
103-
return
104-
end
105-
local key = guide.getKeyName(source)
106-
if type(key) ~= 'string' then
107-
return
108-
end
109-
local uri = guide.getUri(source)
110-
local parentNode = vm.compileNode(source.node)
111-
for pn in parentNode:eachObject() do
112-
searchFieldSwitch(pn.type, uri, pn, key, pushResult)
113-
end
114-
end)
115-
: case 'doc.see.field'
116-
: call(function (source, lastKey, pushResult)
117-
if lastKey then
118-
return
119-
end
120-
local parentNode = vm.compileNode(source.parent.name)
121-
local uri = guide.getUri(source)
122-
for pn in parentNode:eachObject() do
123-
searchFieldSwitch(pn.type, uri, pn, source[1], pushResult)
124-
end
125-
end)
126-
12723
---@param source parser.object
12824
---@param pushResult fun(src: parser.object)
12925
local function searchBySimple(source, pushResult)
@@ -142,25 +38,6 @@ local function searchByLocalID(source, pushResult)
14238
end
14339
end
14440

145-
---@param source parser.object
146-
---@param pushResult fun(src: parser.object)
147-
local function searchByParentNode(source, pushResult)
148-
local lastKey
149-
local src = source
150-
while true do
151-
local key, node = nodeSwitch(src.type, src, lastKey, pushResult)
152-
if not key then
153-
break
154-
end
155-
src = node
156-
if lastKey then
157-
lastKey = key .. vm.ID_SPLITE .. lastKey
158-
else
159-
lastKey = key
160-
end
161-
end
162-
end
163-
16441
local function searchByNode(source, pushResult)
16542
local node = vm.compileNode(source)
16643
local suri = guide.getUri(source)
@@ -200,7 +77,7 @@ function vm.getDefs(source)
20077

20178
searchBySimple(source, pushResult)
20279
searchByLocalID(source, pushResult)
203-
searchByParentNode(source, pushResult)
80+
vm.compileByNodeChain(source, pushResult)
20481
searchByNode(source, pushResult)
20582

20683
return results

test/definition/luadoc.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,4 +932,28 @@ local <!b!>
932932
b.<!<?__index?>!> = b
933933
]]
934934

935+
TEST [[
936+
---@class myClass
937+
local myClass = { nested = {} }
938+
939+
function myClass.nested.<!fn!>() end
940+
941+
---@type myClass
942+
local class
943+
944+
class.nested.<?fn?>()
945+
]]
946+
947+
TEST [[
948+
---@class myClass
949+
local myClass = { has = { nested = {} } }
950+
951+
function myClass.has.nested.<!fn!>() end
952+
953+
---@type myClass
954+
local class
955+
956+
class.has.nested.<?fn?>()
957+
]]
958+
935959
config.set(nil, 'Lua.type.castNumberToInteger', true)

test/type_inference/init.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,3 +3791,15 @@ TEST 'A|B' [[
37913791
---@type A|B
37923792
local <?t?>
37933793
]]
3794+
3795+
TEST 'function' [[
3796+
---@class myClass
3797+
local myClass = { has = { nested = {} } }
3798+
3799+
function myClass.has.nested.fn() end
3800+
3801+
---@type myClass
3802+
local class
3803+
3804+
class.has.nested.<?fn?>()
3805+
]]

0 commit comments

Comments
 (0)