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
|