Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds table field checks #65

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docsrc/warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ Code Description
312 Value of an argument is unused.
313 Value of a loop variable is unused.
314 Value of a field in a table literal is unused.
315 Unused table field
321 Accessing uninitialized local variable.
325 Accessing unitialized table field
331 Value assigned to a local variable is mutated but never accessed.
341 Mutating uninitialized local variable.
411 Redefining a local variable.
Expand Down
1 change: 1 addition & 0 deletions luacheck-dev-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ build = {
["luacheck.stages.detect_empty_blocks"] = "src/luacheck/stages/detect_empty_blocks.lua",
["luacheck.stages.detect_empty_statements"] = "src/luacheck/stages/detect_empty_statements.lua",
["luacheck.stages.detect_globals"] = "src/luacheck/stages/detect_globals.lua",
["luacheck.stages.check_table_fields"] = "src/luacheck/stages/check_table_fields.lua",
["luacheck.stages.detect_reversed_fornum_loops"] = "src/luacheck/stages/detect_reversed_fornum_loops.lua",
["luacheck.stages.detect_unbalanced_assignments"] = "src/luacheck/stages/detect_unbalanced_assignments.lua",
["luacheck.stages.detect_uninit_accesses"] = "src/luacheck/stages/detect_uninit_accesses.lua",
Expand Down
17 changes: 9 additions & 8 deletions spec/cli_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -768,18 +768,19 @@ Total: 8 warnings / 3 errors in 2 files

it("shows correct ranges for files with utf8", function()
assert.equal([[
Checking spec/samples/utf8.lua 4 warnings
Checking spec/samples/utf8.lua 5 warnings

spec/samples/utf8.lua:2:1-4: setting undefined field '분야 명' of global 'math'
spec/samples/utf8.lua:2:16-19: accessing undefined field '値' of global 'math'
spec/samples/utf8.lua:3:25-25: unused variable 't'
spec/samples/utf8.lua:4:5-28: value assigned to field 'päällekkäinen nimi a\u{200B}b' is overwritten on line 5 before use
spec/samples/utf8.lua:5:5-28: value assigned to table field 't'.'päällekkäinen nimi a​b' is unused

Checking spec/samples/utf8_error.lua 1 error

spec/samples/utf8_error.lua:2:11-11: expected statement near 'о'

Total: 4 warnings / 1 error in 2 files
Total: 5 warnings / 1 error in 2 files
]], get_output "spec/samples/utf8.lua spec/samples/utf8_error.lua --ranges --no-config")
end)

Expand Down Expand Up @@ -1250,10 +1251,10 @@ Checking spec/samples/redefined.lua 7 warnings
Checking spec/samples/reversed_fornum.lua 1 warning
Checking spec/samples/unused_code.lua 10 warnings
Checking spec/samples/unused_secondaries.lua 4 warnings
Checking spec/samples/utf8.lua 4 warnings
Checking spec/samples/utf8.lua 5 warnings
Checking spec/samples/utf8_error.lua 1 error

Total: 75 warnings / 9 errors in 21 files
Total: 76 warnings / 9 errors in 21 files
]]):gsub("(spec/samples)/", "%1"..package.config:sub(1, 1)),
get_output "spec/samples --config=spec/configs/exclude_files_config.luacheckrc -qq --exclude-files spec/samples/global_fields.lua")
end)
Expand All @@ -1277,10 +1278,10 @@ Checking redefined.lua 7 warnings
Checking reversed_fornum.lua 1 warning
Checking unused_code.lua 10 warnings
Checking unused_secondaries.lua 4 warnings
Checking utf8.lua 4 warnings
Checking utf8.lua 5 warnings
Checking utf8_error.lua 1 error

Total: 75 warnings / 9 errors in 21 files
Total: 76 warnings / 9 errors in 21 files
]], get_output(". --config=spec/configs/exclude_files_config.luacheckrc -qq --exclude-files global_fields.lua", "spec/samples/"))
end)

Expand All @@ -1301,10 +1302,10 @@ Checking redefined.lua 7 warnings
Checking reversed_fornum.lua 1 warning
Checking unused_code.lua 10 warnings
Checking unused_secondaries.lua 4 warnings
Checking utf8.lua 4 warnings
Checking utf8.lua 5 warnings
Checking utf8_error.lua 1 error

Total: 67 warnings / 9 errors in 19 files
Total: 68 warnings / 9 errors in 19 files
]], get_output(". --config=spec/configs/exclude_files_config.luacheckrc -qq --exclude-files global_fields.lua --exclude-files " .. quote("./read*"), "spec/samples/"))
end)

Expand Down
184 changes: 184 additions & 0 deletions spec/table_field_limitations_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
local helper = require "spec.helper"

local function assert_warnings(warnings, src)
assert.same(warnings, helper.get_stage_warnings("check_table_fields", src))
end

describe("table field todo tests", function()
it("does nothing for globals", function()
assert_warnings({}, [[
x = {}
x[1] = 1
x[2] = x.y
x[1] = 1

y[1] = 1
y[2] = x.y
y[1] = 1
]])
end)

it("can't parse complicated values out", function()
assert_warnings({}, [[
local val = nil
local t = {}
t[1] = val
print(t[1])
]])
end)

it("does nothing for nested tables", function()
assert_warnings({}, [[
local x = {}
x[1] = {}
x[1][1] = 1
x[1][1] = x[1][2]
return x
]])
end)

-- Because of possible multiple return
it("assumes tables initialized from functions can have arbitrary keys set", function()
assert_warnings({
{code = "315", line = 3, column = 3, end_column = 3, name = 'x', field = 'y', set_is_nil = ''},
}, [[
local function func() return 1 end
local x = {func()}
x.y = x[2]
]])
end)

it("does nothing for table parameters that aren't declared in scope", function()
assert_warnings({}, [[
function func(x)
x[1] = x.z
x[1] = 1
end
]])
end)

it("doesn't handle metatables", function()
assert_warnings({}, [[
local x = setmetatable({}, {})
x[1] = 1
print(x[2])
]])
end)

it("detects unused and undefined table fields inside control blocks, but not between them", function()
assert_warnings({
{line = 4, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
{line = 10, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
{line = 16, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
{line = 22, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
{line = 28, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
{line = 34, column = 13, name = 'x', end_column = 13, field = 'z', code = '325', },
}, [[
do
local x = {}
x.y = 1
x[1] = x.z
end

if true then
local x = {}
x.y = 1
x[1] = x.z
end

while true do
local x = {}
x.y = 1
x[1] = x.z
end

repeat
local x = {}
x.y = 1
x[1] = x.z
until false

for i=1,2 do
local x = {}
x.y = 1
x[1] = x.z
end

for _,_ in pairs({}) do
local x = {}
x.y = 1
x[1] = x.z
end
]])
end)

it("stops checking referenced upvalues if function call is known to not have table as an upvalue", function()
assert_warnings({}, [[
local x = {}
x[1] = 1
local function printx() x = 1 end
local function ret2() return 2 end
ret2()
x[1] = 1

local y = {}
y[1] = 1
function y.printx() y = 1 end
function y.ret2() return 2 end
y.ret2()
y[1] = 1
]])
end)

it("halts checking at the end of control flow blocks with jumps", function()
assert_warnings({}, [[
local x = {1}
if math.rand(0,1) ~= 1 then
x = {}
end

x[1] = x[1]

local y = {1}
if math.random(0,1) == 1 then
y[1] = 2
else
y = {}
end

y[1] = y[1]

local a = {1}
while math.random(0,1) == 1 do
a = {}
end

a[1] = a[1]
]])
end)

it("stops checking if a function is called", function()
assert_warnings({
{line = 8, column = 3, name = 'y', end_column = 3, field = 'x', code = '315', set_is_nil = '' },
{line = 8, column = 9, name = 'y', end_column = 9, field = 'a', code = '325', },
{line = 14, column = 9, name = 't', end_column = 9, field = 'a', code = '325', },
}, [[
local x = {}
x.y = 1
print("Unrelated text")
x.y = 2
x[1] = x.z

local y = {}
y.x = y.a
y.x = 1
function y:func() return 1 end
y:func()

local t = {}
t.x = t.a
local var = 'func'
t.x = y[var]() + 1
]])
end)
end)
Loading
Loading