summaryrefslogtreecommitdiff
path: root/script/core/diagnostics/duplicate-doc-field.lua
blob: 098b41a489da3ec2c9089ca4ea8f9f1cc9af1f5f (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
local files   = require 'files'
local lang    = require 'language'
local vm      = require 'vm.vm'
local await   = require 'await'
local guide   = require 'parser.guide'

local function getFieldEventName(doc)
    if not doc.extends then
        return nil
    end
    if #doc.extends.types ~= 1 then
        return nil
    end
    local docFunc = doc.extends.types[1]
    if docFunc.type ~= 'doc.type.function' then
        return nil
    end
    for i = 1, 2 do
        local arg = docFunc.args[i]
        if  arg
        and arg.extends
        and #arg.extends.types == 1 then
            local literal = arg.extends.types[1]
            if literal.type == 'doc.type.boolean'
            or literal.type == 'doc.type.string'
            or literal.type == 'doc.type.integer' then
                return ('%q'):format(literal[1])
            end
        end
    end
    return nil
end

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

    if not state.ast.docs then
        return
    end

    local cachedKeys = {}

    ---@param field parser.object
    ---@return string?
    local function viewKey(field)
        if not cachedKeys[field] then
            local view = vm.viewKey(field, uri)
            if view then
                local eventName = getFieldEventName(field)
                if eventName then
                    view = view .. '|' .. eventName
                end
            end
            cachedKeys[field] = view or false
        end
        return cachedKeys[field] or nil
    end

    ---@async
    ---@param myField parser.object
    local function checkField(myField)
        await.delay()
        local myView = viewKey(myField)
        if not myView then
            return
        end

        local class = myField.class
        if not class then
            return
        end
        for _, set in ipairs(vm.getGlobal('type', class.class[1]):getSets(uri)) do
            if not set.fields then
                goto CONTINUE
            end
            for _, field in ipairs(set.fields) do
                if field == myField then
                    goto CONTINUE
                end
                local view = viewKey(field)
                if view ~= myView then
                    goto CONTINUE
                end
                callback {
                    start   = myField.field.start,
                    finish  = myField.field.finish,
                    message = lang.script('DIAG_DUPLICATE_DOC_FIELD', myView),
                    related = {{
                        start  = field.field.start,
                        finish = field.field.finish,
                        uri    = guide.getUri(field),
                    }}
                }
                do return end
                ::CONTINUE::
            end
            ::CONTINUE::
        end
    end

    for _, doc in ipairs(state.ast.docs) do
        if doc.type == 'doc.field' then
            checkField(doc)
        end
    end
end