summaryrefslogtreecommitdiff
path: root/script/core/diagnostics/unused-local.lua
blob: 52622eb2483777f854233d10c4813f3c5818a584 (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
local files  = require 'files'
local guide  = require 'parser.guide'
local define = require 'proto.define'
local lang   = require 'language'
local vm     = require 'vm.vm'
local config = require 'config.config'
local glob   = require 'glob'

local function hasGet(loc)
    if not loc.ref then
        return false
    end
    local weak
    for _, ref in ipairs(loc.ref) do
        if ref.type == 'getlocal' then
            if not ref.next then
                return 'strong'
            end
            local nextType = ref.next.type
            if  nextType ~= 'setmethod'
            and nextType ~= 'setfield'
            and nextType ~= 'setindex' then
                return 'strong'
            else
                weak = true
            end
        end
    end
    if weak then
        return 'weak'
    else
        return nil
    end
end

local function isMyTable(loc)
    local value = loc.value
    if value and value.type == 'table' then
        return true
    end
    return false
end

local function isToBeClosed(source)
    if not source.attrs then
        return false
    end
    for _, attr in ipairs(source.attrs) do
        if attr[1] == 'close' then
            return true
        end
    end
    return false
end

local function isDocClass(source)
    if not source.bindDocs then
        return false
    end
    for _, doc in ipairs(source.bindDocs) do
        if doc.type == 'doc.class' then
            return true
        end
    end
    return false
end

---@param source parser.object
local function isDeclareFunctionParam(source)
    if source.parent.type ~= 'funcargs' then
        return false
    end
    local func = source.parent.parent
    return vm.isEmptyFunction(func)
end

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

    local isMeta = vm.isMetaFile(uri)
    local ignorePatterns = config.get(uri, 'Lua.diagnostics.unusedLocalExclude')
    local ignore = glob.glob(ignorePatterns)
    guide.eachSourceType(ast.ast, 'local', function (source)
        local name = source[1]
        if name == '_'
        or name == ast.ENVMode then
            return
        end
        if ignore(name) then
            return
        end
        if isToBeClosed(source) then
            return
        end
        if isDocClass(source) then
            return
        end
        if isMeta and isDeclareFunctionParam(source) then
            return
        end
        local data = hasGet(source)
        if data == 'strong' then
            return
        end
        if data == 'weak' then
            if not isMyTable(source) then
                return
            end
        end
        callback {
            start   = source.start,
            finish  = source.finish,
            tags    = { define.DiagnosticTag.Unnecessary },
            message = lang.script('DIAG_UNUSED_LOCAL', name),
        }
        if source.ref then
            for _, ref in ipairs(source.ref) do
                callback {
                    start   = ref.start,
                    finish  = ref.finish,
                    tags    = { define.DiagnosticTag.Unnecessary },
                    message = lang.script('DIAG_UNUSED_LOCAL', name),
                }
            end
        end
    end)
end