Skip to content

Commit 1dd194d

Browse files
committed
Reapply "Fix type inference for and/or idioms and circular dependency in tracer"
This reverts commit b968dc7.
1 parent ee5d128 commit 1dd194d

File tree

2 files changed

+67
-8
lines changed

2 files changed

+67
-8
lines changed

script/core/diagnostics/no-unknown.lua

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,29 @@ return function (uri, callback)
2626
guide.eachSourceTypes(ast.ast, types, function (source)
2727
await.delay()
2828
if vm.getInfer(source):view(uri) == 'unknown' then
29-
callback {
30-
start = source.start,
31-
finish = source.finish,
32-
message = lang.script('DIAG_UNKNOWN'),
33-
}
29+
-- When a node only contains a 'variable' object whose base
30+
-- declaration has a known type, this is a false positive caused
31+
-- by circular dependency during compilation, not a true unknown.
32+
local dominated = false
33+
local node = vm.getNode(source)
34+
if node then
35+
for n in node:eachObject() do
36+
if n.type == 'variable' and n.base and n.base.value then
37+
local baseView = vm.getInfer(n.base):view(uri)
38+
if baseView ~= 'unknown' then
39+
dominated = true
40+
break
41+
end
42+
end
43+
end
44+
end
45+
if not dominated then
46+
callback {
47+
start = source.start,
48+
finish = source.finish,
49+
message = lang.script('DIAG_UNKNOWN'),
50+
}
51+
end
3452
end
3553
end)
3654
end

script/vm/tracer.lua

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,9 +638,22 @@ local lookIntoChild = util.switch()
638638
tracer:lookIntoChild(action[2], topNode)
639639
return topNode, outNode
640640
end
641-
if action.op.type == 'and' then
642-
topNode = tracer:lookIntoChild(action[1], topNode, topNode:copy())
643-
topNode = tracer:lookIntoChild(action[2], topNode, topNode:copy())
641+
if action.op.type == 'and' then
642+
local topNode1, outNode1 = tracer:lookIntoChild(action[1], topNode, topNode:copy())
643+
topNode = tracer:lookIntoChild(action[2], topNode1, topNode1:copy())
644+
-- When the right side of `and` is a truthy literal (string, number,
645+
-- true, table, function), the `and` can only be false when the left
646+
-- side is false. Propagate the narrowed outNode so that patterns
647+
-- like `x == nil and "default" or x` correctly infer x as non-nil.
648+
local tp2 = action[2].type
649+
if tp2 == 'string'
650+
or tp2 == 'number'
651+
or tp2 == 'integer'
652+
or tp2 == 'table'
653+
or tp2 == 'function'
654+
or (tp2 == 'boolean' and action[2][1] == true) then
655+
outNode = outNode1
656+
end
644657
elseif action.op.type == 'or' then
645658
outNode = outNode or topNode:copy()
646659
local topNode1, outNode1 = tracer:lookIntoChild(action[1], topNode, outNode)
@@ -844,12 +857,40 @@ function mt:calcNode(source)
844857
return
845858
end
846859
if self.assignMap[source] then
860+
-- Guard against circular dependency: when this setlocal is already
861+
-- being compiled (e.g. if-handler's getNode triggers calcNode for
862+
-- a setlocal whose value is currently being compiled), skip
863+
-- lookIntoBlock to avoid propagating incomplete types and setting
864+
-- marks that would prevent later correct processing.
865+
if self._compilingAssigns and self._compilingAssigns[source] then
866+
self.nodes[source] = vm.compileNode(source)
867+
return
868+
end
869+
if not self._compilingAssigns then
870+
self._compilingAssigns = {}
871+
end
872+
self._compilingAssigns[source] = true
847873
local node = vm.compileNode(source)
874+
-- When the compiled node has no known type (only contains a 'variable'
875+
-- due to circular dependency), fall back to the variable's base
876+
-- declaration node. This prevents incomplete nodes from propagating
877+
-- through control flow analysis (e.g. if-blocks inside for-loops),
878+
-- which would otherwise cause the type to degrade to 'unknown'.
879+
if not node:hasKnownType()
880+
and self.mode == 'local'
881+
and self.source.type == 'variable'
882+
and self.source.base then
883+
local baseNode = vm.compileNode(self.source.base)
884+
if baseNode:hasKnownType() then
885+
node = baseNode
886+
end
887+
end
848888
self.nodes[source] = node
849889
local parentBlock = guide.getParentBlock(source)
850890
if parentBlock then
851891
self:lookIntoBlock(parentBlock, source.finish, node)
852892
end
893+
self._compilingAssigns[source] = nil
853894
return
854895
end
855896
end

0 commit comments

Comments
 (0)