Jianw
2025-05-13 3b39fe3810c3ee2ec9ec97236c1769c5c85e062c
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
local decoder = require "luacheck.decoder"
local utils = require "luacheck.utils"
 
local core_utils = {}
 
-- Attempts to evaluate a node as a Lua value, without resolving locals.
-- Returns Lua value and its string representation on success, nothing on failure.
function core_utils.eval_const_node(node)
   if node.tag == "True" then
      return true, "true"
   elseif node.tag == "False" then
      return false, "false"
   elseif node.tag == "String" then
      local chars = decoder.decode(node[1])
      return node[1], chars:get_printable_substring(1, chars:get_length())
   else
      local is_negative
 
      if node.tag == "Op" and node[1] == "unm" then
         is_negative = true
         node = node[2]
      end
 
      if node.tag ~= "Number" then
         return
      end
 
      local str = node[1]
 
      if str:find("[iIuUlL]") then
         -- Ignore LuaJIT cdata literals.
         return
      end
 
      -- On Lua 5.3 convert to float to get same results as on Lua 5.1 and 5.2.
      if _VERSION == "Lua 5.3" and not str:find("[%.eEpP]") then
         str = str .. ".0"
      end
 
      local number = tonumber(str)
 
      if not number then
         return
      end
 
      if is_negative then
         number = -number
      end
 
      if number == number and number < 1/0 and number > -1/0 then
         return number, (is_negative and "-" or "") .. node[1]
      end
   end
end
 
local statement_containing_tags = utils.array_to_set({"Do", "While", "Repeat", "Fornum", "Forin", "If"})
 
-- `items` is an array of nodes or nested item arrays.
local function scan_for_statements(chstate, items, tags, callback, ...)
   for _, item in ipairs(items) do
      if tags[item.tag] then
         callback(chstate, item, ...)
      end
 
      if not item.tag or statement_containing_tags[item.tag] then
         scan_for_statements(chstate, item, tags, callback, ...)
      end
   end
end
 
-- Calls `callback(chstate, node, ...)` for each statement node within AST with tag in given array.
function core_utils.each_statement(chstate, tags_array, callback, ...)
   local tags = utils.array_to_set(tags_array)
 
   for _, line in ipairs(chstate.lines) do
      scan_for_statements(chstate, line.node[2], tags, callback, ...)
   end
end
 
local function location_comparator(warning1, warning2)
   if warning1.line ~= warning2.line then
      return warning1.line < warning2.line
   elseif warning1.column ~= warning2.column then
      return warning1.column < warning2.column
   else
      return warning1.code < warning2.code
   end
end
 
-- Sorts an array of warnings by location information as provided in `line` and `column` fields.
function core_utils.sort_by_location(warnings)
   table.sort(warnings, location_comparator)
end
 
return core_utils