summaryrefslogtreecommitdiff
path: root/script/core/diagnostics/missing-parameter.lua
blob: 9844046fa2c5ca9adf4d7424cc28633b2bd73ad9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
local files  = require 'files'
local guide  = require 'parser.guide'
local vm     = require 'vm'
local lang   = require 'language'

---@param source parser.object
---@return integer
local function countReturnsOfFunction(source)
    local n = 0

    local docs = source.bindDocs
    if docs then
        for _, doc in ipairs(docs) do
            if doc.type == 'doc.return' then
                for _, rtn in ipairs(doc.returns) do
                    if rtn.returnIndex and rtn.returnIndex > n then
                        n = rtn.returnIndex
                    end
                end
            end
        end
    end

    local returns = source.returns
    if returns then
        for _, rtn in ipairs(returns) do
            if #rtn > n then
                n = #rtn
            end
        end
    end

    return n
end

---@param source parser.object
---@return integer
local function countReturnsOfDocFunction(source)
    return #source.returns
end

local function countMaxReturns(source)
    local hasFounded
    local n = 0
    for _, def in ipairs(vm.getDefs(source)) do
        if def.type == 'function' then
            hasFounded = true
            local rets = countReturnsOfFunction(def)
            if rets > n then
                n = rets
            end
        elseif def.type == 'doc.type.function' then
            hasFounded = true
            local rets = countReturnsOfDocFunction(def)
            if rets > n then
                n = rets
            end
        end
    end

    if hasFounded then
        return n
    else
        return math.huge
    end
end

local function countCallArgs(source)
    local result = 0
    if not source.args then
        return 0
    end
    local lastArg = source.args[#source.args]
    if lastArg.type == 'varargs' then
        return math.huge
    end
    if lastArg.type == 'call' then
        result = result + countMaxReturns(lastArg.node) - 1
    end
    result = result + #source.args
    return result
end

---@return integer
local function countFuncArgs(source)
    if not source.args or #source.args == 0 then
        return 0
    end
    local count = 0
    for i = #source.args, 1, -1 do
        local arg = source.args[i]
        if  arg.type ~= '...'
        and not (arg.name and arg.name[1] =='...')
        and not vm.compileNode(arg):isNullable() then
            return i
        end
    end
    return count
end

local function getFuncArgs(func)
    local funcArgs
    local defs = vm.getDefs(func)
    for _, def in ipairs(defs) do
        if def.type == 'function'
        or def.type == 'doc.type.function' then
            local args = countFuncArgs(def)
            if not funcArgs or args < funcArgs then
                funcArgs = args
            end
        end
    end
    return funcArgs
end

return function (uri, callback)
    local state = files.getState(uri)
    if not state then
        return
    end

    guide.eachSourceType(state.ast, 'call', function (source)
        local callArgs = countCallArgs(source)

        local func = source.node
        local funcArgs = getFuncArgs(func)

        if not funcArgs then
            return
        end

        local delta = callArgs - funcArgs
        if delta >= 0 then
            return
        end
        callback {
            start  = source.start,
            finish = source.finish,
            message = lang.script('DIAG_MISS_ARGS', funcArgs, callArgs),
        }
    end)
end