summaryrefslogtreecommitdiff
path: root/server-beta/src
diff options
context:
space:
mode:
Diffstat (limited to 'server-beta/src')
-rw-r--r--server-beta/src/parser/guide.lua19
-rw-r--r--server-beta/src/searcher/init.lua2
-rw-r--r--server-beta/src/searcher/isTrue.lua52
-rw-r--r--server-beta/src/searcher/searcher.lua4
-rw-r--r--server-beta/src/searcher/typeInference.lua253
5 files changed, 324 insertions, 6 deletions
diff --git a/server-beta/src/parser/guide.lua b/server-beta/src/parser/guide.lua
index 80f9d160..f91c677e 100644
--- a/server-beta/src/parser/guide.lua
+++ b/server-beta/src/parser/guide.lua
@@ -299,7 +299,10 @@ function m.eachSourceContain(ast, offset, callback)
list[len] = nil
if m.isInRange(obj, offset) then
if m.isContain(obj, offset) then
- callback(obj)
+ local res = callback(obj)
+ if res ~= nil then
+ return res
+ end
end
m.addChilds(list, obj, m.childMap)
end
@@ -476,19 +479,23 @@ function m.getKeyName(obj)
elseif tp == 'getfield'
or tp == 'setfield'
or tp == 'tablefield' then
- return 's|' .. obj.field[1]
+ if obj.field then
+ return 's|' .. obj.field[1]
+ end
elseif tp == 'getmethod'
or tp == 'setmethod' then
- return 's|' .. obj.method[1]
+ if obj.method then
+ return 's|' .. obj.method[1]
+ end
elseif tp == 'getindex'
or tp == 'setindex'
or tp == 'tableindex' then
- return m.getKeyName(obj.index)
+ if obj.index then
+ return m.getKeyName(obj.index)
+ end
elseif tp == 'field'
or tp == 'method' then
return 's|' .. obj[1]
- elseif tp == 'index' then
- return m.getKeyName(obj.index)
elseif tp == 'string' then
local s = obj[1]
if s then
diff --git a/server-beta/src/searcher/init.lua b/server-beta/src/searcher/init.lua
index 6ec7bc99..1734c2ea 100644
--- a/server-beta/src/searcher/init.lua
+++ b/server-beta/src/searcher/init.lua
@@ -6,4 +6,6 @@ require 'searcher.getGlobals'
require 'searcher.getLinks'
require 'searcher.getGlobal'
require 'searcher.getLibrary'
+require 'searcher.typeInference'
+require 'searcher.isTrue'
return searcher
diff --git a/server-beta/src/searcher/isTrue.lua b/server-beta/src/searcher/isTrue.lua
new file mode 100644
index 00000000..ba825d43
--- /dev/null
+++ b/server-beta/src/searcher/isTrue.lua
@@ -0,0 +1,52 @@
+local searcher = require 'searcher.searcher'
+
+local function checkLiteral(source)
+ if source.type == 'boolean' then
+ if source[1] == true then
+ return 'true'
+ else
+ return 'false'
+ end
+ end
+ if source.type == 'string' then
+ return 'true'
+ end
+ if source.type == 'number' then
+ return 'true'
+ end
+ if source.type == 'table' then
+ return 'true'
+ end
+ if source.type == 'function' then
+ return 'true'
+ end
+ if source.type == 'nil' then
+ return 'false'
+ end
+end
+
+local function isTrue(source)
+ local res = checkLiteral(source)
+ if res then
+ return res
+ end
+ return 'unknown'
+end
+
+function searcher.isTrue(source)
+ if not source then
+ return
+ end
+ local cache = searcher.cache.isTrue[source]
+ if cache ~= nil then
+ return cache
+ end
+ local unlock = searcher.lock('isTrue', source)
+ if not unlock then
+ return
+ end
+ cache = isTrue(source) or false
+ searcher.cache.isTrue[source] = cache
+ unlock()
+ return cache
+end
diff --git a/server-beta/src/searcher/searcher.lua b/server-beta/src/searcher/searcher.lua
index 19e114d6..3e12430b 100644
--- a/server-beta/src/searcher/searcher.lua
+++ b/server-beta/src/searcher/searcher.lua
@@ -64,6 +64,8 @@ function m.refreshCache()
getGlobal = {},
specialName = {},
getLibrary = {},
+ typeInfer = {},
+ isTrue = {},
specials = nil,
}
m.locked = {
@@ -72,6 +74,8 @@ function m.refreshCache()
getGlobals = {},
getLinks = {},
getLibrary = {},
+ typeInfer = {},
+ isTrue = {},
}
m.cacheTracker[m.cache] = true
end
diff --git a/server-beta/src/searcher/typeInference.lua b/server-beta/src/searcher/typeInference.lua
new file mode 100644
index 00000000..56f4cc98
--- /dev/null
+++ b/server-beta/src/searcher/typeInference.lua
@@ -0,0 +1,253 @@
+local searcher = require 'searcher.searcher'
+local guide = require 'parser.guide'
+local config = require 'config'
+
+local typeSort = {
+ ['boolean'] = 1,
+ ['string'] = 2,
+ ['integer'] = 3,
+ ['number'] = 4,
+ ['table'] = 5,
+ ['function'] = 6,
+ ['nil'] = math.maxinteger,
+}
+
+local function merge(result, tp)
+ if result[tp] then
+ return
+ end
+ if tp:find('|', 1, true) then
+ for sub in tp:gmatch '[^|]+' do
+ if not result[sub] then
+ result[sub] = true
+ result[#result+1] = sub
+ end
+ end
+ else
+ result[tp] = true
+ result[#result+1] = tp
+ end
+end
+
+local function hasType(tp, target)
+ if not target then
+ return false
+ end
+ for sub in target:gmatch '[^|]+' do
+ if sub == tp then
+ return true
+ end
+ end
+ return false
+end
+
+local function dump(result)
+ if #result <= 1 then
+ return result[1]
+ end
+ table.sort(result, function (a, b)
+ local sa = typeSort[a]
+ local sb = typeSort[b]
+ if sa and sb then
+ return sa < sb
+ end
+ if not sa and not sb then
+ return a < b
+ end
+ if sa and not sb then
+ return true
+ end
+ if not sa and sb then
+ return false
+ end
+ return false
+ end)
+ return table.concat(result, '|')
+end
+
+local function checkLiteral(source)
+ if source.type == 'string' then
+ return 'string'
+ elseif source.type == 'nil' then
+ return 'nil'
+ elseif source.type == 'boolean' then
+ return 'boolean'
+ elseif source.type == 'number' then
+ if math.type(source[1]) == 'integer' then
+ return 'integer'
+ else
+ return 'number'
+ end
+ elseif source.type == 'table' then
+ return 'table'
+ elseif source.type == 'function' then
+ return 'function'
+ end
+end
+
+local function checkUnary(source)
+ if source.type ~= 'unary' then
+ return
+ end
+ local op = source.op
+ if op.type == 'not' then
+ return 'boolean'
+ elseif op.type == '#'
+ or op.type == '~' then
+ return 'integer'
+ elseif op.type == '-' then
+ if hasType('integer', searcher.typeInference(source[1])) then
+ return 'integer'
+ else
+ return 'number'
+ end
+ end
+end
+
+local function checkBinary(source)
+ if source.type ~= 'binary' then
+ return
+ end
+ local op = source.op
+ if op.type == 'and' then
+ local isTrue = searcher.isTrue(source[1])
+ if isTrue == 'true' then
+ return searcher.typeInference(source[2])
+ elseif isTrue == 'false' then
+ return searcher.typeInference(source[1])
+ else
+ local result = {}
+ merge(result, searcher.typeInference(source[1]))
+ merge(result, searcher.typeInference(source[2]))
+ return dump(result)
+ end
+ end
+ if op.type == 'or' then
+ local isTrue = searcher.isTrue(source[1])
+ if isTrue == 'true' then
+ return searcher.typeInference(source[1])
+ elseif isTrue == 'false' then
+ return searcher.typeInference(source[2])
+ else
+ local result = {}
+ merge(result, searcher.typeInference(source[1]))
+ merge(result, searcher.typeInference(source[2]))
+ return dump(result)
+ end
+ end
+ if op.type == '=='
+ or op.type == '~='
+ or op.type == '<='
+ or op.type == '>='
+ or op.type == '<'
+ or op.type == '>' then
+ return 'boolean'
+ end
+ if op.type == '|'
+ or op.type == '~'
+ or op.type == '&'
+ or op.type == '<<'
+ or op.type == '>>' then
+ return 'integer'
+ end
+ if op.type == '..' then
+ return 'string'
+ end
+ if op.type == '^'
+ or op.type == '/' then
+ return 'number'
+ end
+ -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数
+ if op.type == '+'
+ or op.type == '-'
+ or op.type == '*'
+ or op.type == '%'
+ or op.type == '//' then
+ if hasType('integer', searcher.typeInference(source[1]))
+ and hasType('integer', searcher.typeInference(source[2])) then
+ return 'integer'
+ else
+ return 'number'
+ end
+ end
+end
+
+local function checkValue(source)
+ if source.value then
+ return searcher.typeInference(source.value)
+ end
+end
+
+local function checkCall(result, source)
+ if not source.parent then
+ return
+ end
+ if source.parent.type ~= 'call' then
+ return
+ end
+ if source.parent.node == source then
+ merge(result, 'function')
+ return
+ end
+end
+
+local function checkNext(result, source)
+ local next = source.next
+ if not next then
+ return
+ end
+ if next.type == 'getfield'
+ or next.type == 'getindex'
+ or next.type == 'getmethod'
+ or next.type == 'setfield'
+ or next.type == 'setindex'
+ or next.type == 'setmethod' then
+ merge(result, 'table')
+ end
+end
+
+local function checkDef(result, source)
+ searcher.eachDef(source, function (info)
+ local src = info.source
+ local tp = searcher.typeInference(src)
+ if tp then
+ merge(result, tp)
+ end
+ end)
+end
+
+local function typeInference(source)
+ local tp = checkLiteral(source)
+ or checkValue(source)
+ or checkUnary(source)
+ or checkBinary(source)
+ if tp then
+ return tp
+ end
+
+ local result = {}
+
+ checkCall(result, source)
+ checkNext(result, source)
+ checkDef(result, source)
+
+ return dump(result)
+end
+
+function searcher.typeInference(source)
+ if not source then
+ return
+ end
+ local cache = searcher.cache.typeInfer[source]
+ if cache ~= nil then
+ return cache
+ end
+ local unlock = searcher.lock('typeInfer', source)
+ if not unlock then
+ return
+ end
+ cache = typeInference(source) or false
+ searcher.cache.typeInfer[source] = cache
+ unlock()
+ return cache
+end