From 67a341f3fa26e5a3140cde3420ef2269a84d478f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=80=E8=90=8C=E5=B0=8F=E6=B1=90?= <sumneko@hotmail.com>
Date: Fri, 8 Apr 2022 14:35:56 +0800
Subject: cleanup

---
 script/core/diagnostics/undefined-field.lua |  7 ++---
 script/vm/compiler.lua                      | 43 +++++++++++++++++++++--------
 script/vm/node.lua                          |  9 +++++-
 test/diagnostics/common.lua                 |  6 ++++
 test/type_inference/init.lua                |  6 ++++
 5 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index fac9607d..025c217a 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -3,6 +3,7 @@ local vm      = require 'vm'
 local lang    = require 'language'
 local guide   = require 'parser.guide'
 local await   = require 'await'
+local infer   = require 'vm.infer'
 
 local skipCheckClass = {
     ['unknown']       = true,
@@ -33,11 +34,9 @@ return function (uri, callback)
         end
         local node = src.node
         if node then
-            local defs = vm.getDefs(node)
             local ok
-            for _, def in ipairs(defs) do
-                if  def.type == 'doc.class'
-                and not skipCheckClass[def.class[1]] then
+            for view in infer.getInfer(node):eachView() do
+                if not skipCheckClass[view] then
                     ok = true
                     break
                 end
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 43c7be1e..a87f2eba 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -443,6 +443,9 @@ function m.compileByParentNode(source, key, pushResult)
 end
 
 local function selectNode(source, list, index)
+    if not list then
+        return nil
+    end
     local exp
     if list[index] then
         exp = list[index]
@@ -644,17 +647,6 @@ local compilerSwitch = util.switch()
         if source.bindDocs then
             hasMarkDoc = bindDocs(source)
         end
-        if source.ref and not hasMarkDoc then
-            for _, ref in ipairs(source.ref) do
-                if ref.type == 'setlocal' then
-                    if ref.value and ref.value.type == 'table' then
-                        nodeMgr.setNode(source, ref.value)
-                    else
-                        nodeMgr.setNode(source, m.compileNode(ref.value))
-                    end
-                end
-            end
-        end
         local hasMarkParam
         if source.dummy and not hasMarkDoc then
             hasMarkParam = true
@@ -669,6 +661,19 @@ local compilerSwitch = util.switch()
                 end
             end
         end
+        if  not source.value
+        and source.ref
+        and not hasMarkDoc then
+            for _, ref in ipairs(source.ref) do
+                if ref.type == 'setlocal' then
+                    if ref.value and ref.value.type == 'table' then
+                        nodeMgr.setNode(source, ref.value)
+                    else
+                        nodeMgr.setNode(source, m.compileNode(ref.value))
+                    end
+                end
+            end
+        end
         -- function x.y(self, ...) --> function x:y(...)
         if  source[1] == 'self'
         and not hasMarkDoc
@@ -707,11 +712,25 @@ local compilerSwitch = util.switch()
         if source.parent.type == 'loop' then
             nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
         end
+
+        -- avoid self reference
+        -- `local x; x = x`
+        -- the third `x` is unknown here
+        -- x[1] -> value of x[2] -> x[3] -> x[1](locked!)
+        if source.ref then
+            local myNode = nodeMgr.getNode(source)
+            for _, ref in ipairs(source.ref) do
+                if ref.type == 'setlocal'
+                or ref.type == 'getlocal' then
+                    nodeMgr.setNode(ref, myNode, true)
+                end
+            end
+        end
     end)
     : case 'setlocal'
     : case 'getlocal'
     : call(function (source)
-        nodeMgr.setNode(source, m.compileNode(source.node))
+        nodeMgr.setNode(source, m.compileNode(source.node), true)
     end)
     : case 'setfield'
     : case 'setmethod'
diff --git a/script/vm/node.lua b/script/vm/node.lua
index d51c2318..409841fc 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -23,7 +23,14 @@ function m.mergeNode(a, b)
     return union(a, b)
 end
 
-function m.setNode(source, node)
+---@param source parser.object
+---@param node vm.node
+---@param cover? boolean
+function m.setNode(source, node, cover)
+    if cover then
+        m.nodeCache[source] = node
+        return
+    end
     if not node then
         return
     end
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
index 37ea6bc7..f79f1872 100644
--- a/test/diagnostics/common.lua
+++ b/test/diagnostics/common.lua
@@ -1377,3 +1377,9 @@ TEST [[
 TEST [[
 ---@class A 1
 ]]
+
+TEST [[
+local value
+value = '1'
+value = value:gsub()
+]]
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index 5ac5bece..591d9f7c 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -1390,3 +1390,9 @@ TEST 'integer' [[
 local t
 t.<?x?>
 ]]
+
+TEST 'boolean' [[
+local <?var?> = true
+var = 1
+var = 1.0
+]]
-- 
cgit v1.2.3