Skip to content

Commit 763a34f

Browse files
authored
Merge branch 'master' into master
2 parents 8159918 + 5e10c03 commit 763a34f

18 files changed

Lines changed: 348 additions & 300 deletions

File tree

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
## Unreleased
44
<!-- Add all new changes here. They will be moved under a version at release -->
5+
* `CHG` Always track symbol-links
56
* `CHG` Modified the `ResolveRequire` function to pass the source URI as a third argument.
7+
* `CHG` Improved the output of test failures during development
8+
* `FIX` Fix type inference for bitwise operators (`<<`, `>>`, `&`, `|`, `~`) on integer variables
9+
* `FIX` Fix constant value computation for chained bitwise expressions in hover tooltips
10+
* `FIX` Support Lua 5.5 prefix local attributes such as `local <close>x` and `local <const>x`
611
* `FIX` Correct `os` and `io` meta return types.
712

813
## 3.17.1

script/files.lua

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -80,39 +80,7 @@ end
8080
---@param uri uri
8181
---@return uri
8282
function m.getRealUri(uri)
83-
if platform.os ~= 'windows' then
84-
return furi.normalize(uri)
85-
end
86-
if not furi.isValid(uri) then
87-
return uri
88-
end
89-
local filename = furi.decode(uri)
90-
-- normalize uri
91-
uri = furi.encode(filename)
92-
local path = fs.path(filename)
93-
local suc, exists = pcall(fs.exists, path)
94-
if not suc or not exists then
95-
return uri
96-
end
97-
local suc, res = pcall(fs.canonical, path)
98-
if not suc then
99-
return uri
100-
end
101-
filename = res:string()
102-
local ruri = furi.encode(filename)
103-
if uri == ruri then
104-
return ruri
105-
end
106-
local real = getRealParent(path:parent_path()) / res:filename()
107-
ruri = furi.encode(real:string())
108-
if uri == ruri then
109-
return ruri
110-
end
111-
if not uriMap[uri] then
112-
uriMap[uri] = true
113-
log.warn(('Fix real file uri: %s -> %s'):format(uri, ruri))
114-
end
115-
return ruri
83+
return furi.normalize(uri)
11684
end
11785

11886
--- 打开文件

script/parser/compile.lua

Lines changed: 113 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,8 @@ local function expectAssign(isAction)
652652
return false
653653
end
654654

655-
local function parseLocalAttrs()
655+
---@param kind? '"prefix"'|'"suffix"'
656+
local function parseLocalAttrs(kind)
656657
local attrs
657658
while true do
658659
skipSpace()
@@ -672,6 +673,7 @@ local function parseLocalAttrs()
672673
parent = attrs,
673674
start = getPosition(Tokens[Index], 'left'),
674675
finish = getPosition(Tokens[Index], 'right'),
676+
kind = kind or 'suffix',
675677
}
676678
attrs[#attrs+1] = attr
677679
Index = Index + 2
@@ -725,6 +727,20 @@ local function parseLocalAttrs()
725727
return attrs
726728
end
727729

730+
local function mergeLocalAttrs(attrsBefore, attrsAfter)
731+
if not attrsBefore then
732+
return attrsAfter
733+
end
734+
if not attrsAfter then
735+
return attrsBefore
736+
end
737+
for i = 1, #attrsAfter do
738+
attrsBefore[#attrsBefore + 1] = attrsAfter[i]
739+
end
740+
attrsBefore.finish = attrsAfter[#attrsAfter].finish
741+
return attrsBefore
742+
end
743+
728744
---@param obj table
729745
local function createLocal(obj, attrs)
730746
obj.type = 'local'
@@ -951,6 +967,79 @@ local function hasAttr(attrs, attrName)
951967
return false
952968
end
953969

970+
local function hasAttrKind(attrs, attrName, attrKind)
971+
if not attrs then
972+
return false
973+
end
974+
for i = 1, #attrs do
975+
if attrs[i][1] == attrName and attrs[i].kind == attrKind then
976+
return true
977+
end
978+
end
979+
return false
980+
end
981+
982+
local function checkLocalCloseList(n1, n2, nrest)
983+
if State.version ~= 'Lua 5.4' and State.version ~= 'Lua 5.5' then
984+
return
985+
end
986+
987+
if State.version == 'Lua 5.5' and hasAttrKind(n1 and n1.attrs, 'close', 'prefix') then
988+
local extra
989+
if n2 and not n2.attrs then
990+
extra = n2
991+
elseif nrest then
992+
for i = 1, #nrest do
993+
if not nrest[i].attrs then
994+
extra = nrest[i]
995+
break
996+
end
997+
end
998+
end
999+
if extra then
1000+
pushError {
1001+
type = 'MULTI_CLOSE',
1002+
start = extra.start,
1003+
finish = extra.finish,
1004+
}
1005+
return
1006+
end
1007+
end
1008+
1009+
local function collectCloseAttrs(node, list)
1010+
local attrs = node and node.attrs
1011+
if not attrs then
1012+
return
1013+
end
1014+
for i = 1, #attrs do
1015+
local a = attrs[i]
1016+
if a[1] == 'close' then
1017+
list[#list + 1] = a
1018+
end
1019+
end
1020+
end
1021+
1022+
local closeList = {}
1023+
collectCloseAttrs(n1, closeList)
1024+
if n2 then
1025+
collectCloseAttrs(n2, closeList)
1026+
end
1027+
if nrest then
1028+
for i = 1, #nrest do
1029+
collectCloseAttrs(nrest[i], closeList)
1030+
end
1031+
end
1032+
1033+
if #closeList > 1 then
1034+
local second = closeList[2]
1035+
pushError {
1036+
type = 'MULTI_CLOSE',
1037+
start = second.start,
1038+
finish = second.finish,
1039+
}
1040+
end
1041+
end
1042+
9541043
local function resolveLable(label, obj)
9551044
if not label.ref then
9561045
label.ref = {}
@@ -3208,43 +3297,8 @@ local function parseMultiVars(n1, parser, isLocal)
32083297
end
32093298
end
32103299

3211-
do
3212-
-- Lua 5.4: only one <close> attribute allowed across a local declaration
3213-
-- Lua 5.5: multiple <close> are allowed
3214-
if State.version == 'Lua 5.4' then
3215-
local function collectCloseAttrs(node, list)
3216-
local attrs = node and node.attrs
3217-
if not attrs then
3218-
return
3219-
end
3220-
for i = 1, #attrs do
3221-
local a = attrs[i]
3222-
if a[1] == 'close' then
3223-
list[#list + 1] = a
3224-
end
3225-
end
3226-
end
3227-
3228-
local closeList = {}
3229-
collectCloseAttrs(n1, closeList)
3230-
if n2 then
3231-
collectCloseAttrs(n2, closeList)
3232-
end
3233-
if nrest then
3234-
for i = 1, #nrest do
3235-
collectCloseAttrs(nrest[i], closeList)
3236-
end
3237-
end
3238-
3239-
if #closeList > 1 then
3240-
local second = closeList[2]
3241-
pushError {
3242-
type = 'MULTI_CLOSE',
3243-
start = second.start,
3244-
finish = second.finish,
3245-
}
3246-
end
3247-
end
3300+
if isLocal then
3301+
checkLocalCloseList(n1, n2, nrest)
32483302
end
32493303

32503304
if v2 and not n2 then
@@ -3343,13 +3397,30 @@ local function parseLocal()
33433397
local locPos = getPosition(Tokens[Index], 'left')
33443398
Index = Index + 2
33453399
skipSpace()
3346-
local word = peekWord()
3400+
local attrsBefore = parseLocalAttrs('prefix')
3401+
if attrsBefore and State.version ~= 'Lua 5.5' then
3402+
pushError {
3403+
type = 'UNSUPPORT_SYMBOL',
3404+
start = attrsBefore.start,
3405+
finish = attrsBefore.finish,
3406+
version = 'Lua 5.5',
3407+
}
3408+
end
3409+
skipSpace()
3410+
local word, wstart, wfinish = peekWord()
33473411
if not word then
33483412
missName()
33493413
return nil
33503414
end
33513415

33523416
if word == 'function' then
3417+
if attrsBefore then
3418+
pushError {
3419+
type = 'MISS_NAME',
3420+
start = wstart,
3421+
finish = wfinish,
3422+
}
3423+
end
33533424
local func = parseFunction('local', true)
33543425
local name = func.name
33553426
if name then
@@ -3373,7 +3444,9 @@ local function parseLocal()
33733444
missName()
33743445
return nil
33753446
end
3376-
local loc = createLocal(name, parseLocalAttrs())
3447+
local attrsAfter = parseLocalAttrs('suffix')
3448+
local attrs = mergeLocalAttrs(attrsBefore, attrsAfter)
3449+
local loc = createLocal(name, attrs)
33773450
loc.locPos = locPos
33783451
loc.effect = maxinteger
33793452
pushActionIntoCurrentChunk(loc)

script/vm/operator.lua

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ vm.OP_UNARY_MAP = util.revertMap(unaryMap)
5858
vm.OP_BINARY_MAP = util.revertMap(binaryMap)
5959
vm.OP_OTHER_MAP = util.revertMap(otherMap)
6060

61+
---@param source parser.object
62+
---@return uri, vm.infer, vm.infer
63+
local function getOperandInfers(source)
64+
return guide.getUri(source), vm.getInfer(source[1]), vm.getInfer(source[2])
65+
end
66+
6167
---@param operators parser.object[]
6268
---@param op string
6369
---@param value? parser.object
@@ -267,6 +273,13 @@ vm.binarySwitch = util.switch()
267273
end
268274
if node then
269275
vm.setNode(source, node)
276+
return
277+
end
278+
-- Bitwise ops on integers always produce integer
279+
local uri, infer1, infer2 = getOperandInfers(source)
280+
if infer1:hasType(uri, 'integer')
281+
and infer2:hasType(uri, 'integer') then
282+
vm.setNode(source, vm.declareGlobal('type', 'integer'))
270283
end
271284
end
272285
end)
@@ -386,9 +399,7 @@ vm.binarySwitch = util.switch()
386399
[1] = a .. b,
387400
})
388401
else
389-
local uri = guide.getUri(source)
390-
local infer1 = vm.getInfer(source[1])
391-
local infer2 = vm.getInfer(source[2])
402+
local uri, infer1, infer2 = getOperandInfers(source)
392403
if (
393404
infer1:hasType(uri, 'integer')
394405
or infer1:hasType(uri, 'number')

script/vm/value.lua

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ function vm.getInteger(v)
118118
end
119119
local node = vm.compileNode(v)
120120
local result
121+
local hasNonInteger = false
121122
for n in node:eachObject() do
122123
if n.type == 'integer' then
123124
if result then
@@ -135,7 +136,18 @@ function vm.getInteger(v)
135136
end
136137
elseif n.type ~= 'local'
137138
and n.type ~= 'global' then
138-
return nil
139+
hasNonInteger = true
140+
end
141+
end
142+
if hasNonInteger then
143+
result = nil
144+
end
145+
-- If value not found via compiled node, try the local's
146+
-- definition value directly (tracer may not preserve literals)
147+
if result == nil and v.type == 'getlocal' then
148+
local loc = v.node
149+
if loc and loc.value then
150+
return vm.getInteger(loc.value)
139151
end
140152
end
141153
return result

test/code_action/init.lua

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,21 @@
1-
local core = require 'core.code-action'
2-
local files = require 'files'
3-
local lang = require 'language'
4-
local catch = require 'catch'
5-
local furi = require 'file-uri'
1+
local core = require 'core.code-action'
2+
local files = require 'files'
3+
local lang = require 'language'
4+
local catch = require 'catch'
5+
local furi = require 'file-uri'
6+
local compare = require 'compare'
67

78
rawset(_G, 'TEST', true)
89

9-
local EXISTS = {}
10+
local EXISTS = compare.EXISTS
1011

11-
local function eq(expected, result)
12-
if expected == EXISTS and result ~= nil then
13-
return true
14-
end
15-
if result == EXISTS and expected ~= nil then
16-
return true
17-
end
18-
local tp1, tp2 = type(expected), type(result)
19-
if tp1 ~= tp2 then
20-
return false, string.format(": expected type %s, got %s", tp1, tp2)
21-
end
22-
if tp1 == 'table' then
23-
local mark = {}
24-
for k in pairs(expected) do
25-
local ok, err = eq(expected[k], result[k])
26-
if not ok then
27-
return false, string.format(".%s%s", k, err)
28-
end
29-
mark[k] = true
30-
end
31-
for k in pairs(result) do
32-
if not mark[k] then
33-
return false, string.format(".%s: missing key in result", k)
34-
end
35-
end
36-
return true
37-
end
38-
return expected == result, string.format(": expected %s, got %s", expected, result)
39-
end
40-
41-
function TEST(script)
12+
local function TEST(script)
4213
return function (expect)
4314
local newScript, catched = catch(script, '?')
4415
files.setText(TESTURI, newScript)
4516
local results = core(TESTURI, catched['?'][1][1], catched['?'][1][2])
4617
assert(results)
47-
assert(eq(expect, results))
18+
assert(compare.eq(expect, results))
4819
files.remove(TESTURI)
4920
end
5021
end
@@ -72,7 +43,7 @@ local function TEST_CROSSFILE(testfiles)
7243

7344
local results = core(TESTURI, catched['?'][1][1], catched['?'][1][2])
7445
assert(results)
75-
assert(eq(expected, results))
46+
assert(compare.eq(expected, results))
7647
end
7748
end
7849

0 commit comments

Comments
 (0)