Skip to content

Commit

Permalink
Adds compound operator support for the table field checks
Browse files Browse the repository at this point in the history
  • Loading branch information
arichard4 committed Dec 12, 2024
1 parent 3340ea4 commit af4ca59
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 17 deletions.
28 changes: 12 additions & 16 deletions spec/cli_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -296,36 +296,32 @@ Total: 5 warnings / 0 errors in 1 file
end)

it("raises critical errors on config without additional operators", function()
assert.equal([[Checking spec/samples/compound_operators.lua 1 warning / 4 errors
assert.equal([[Checking spec/samples/compound_operators.lua 4 errors
spec/samples/compound_operators.lua:2:1: assignment uses compound operator +=
spec/samples/compound_operators.lua:3:1: assignment uses compound operator -=
spec/samples/compound_operators.lua:5:2: assignment uses compound operator /=
spec/samples/compound_operators.lua:9:3: value assigned to table field 't'.'a' is unused
spec/samples/compound_operators.lua:10:1: assignment uses compound operator *=
Total: 1 warning / 4 errors in 1 file
Total: 0 warnings / 4 errors in 1 file
]], get_output "spec/samples/compound_operators.lua --no-config")
end)

it("raises critical errors for unfiltered additional operators", function()
assert.equal([[Checking spec/samples/compound_operators.lua 1 warning / 3 errors
assert.equal([[Checking spec/samples/compound_operators.lua 3 errors
spec/samples/compound_operators.lua:3:1: assignment uses compound operator -=
spec/samples/compound_operators.lua:5:2: assignment uses compound operator /=
spec/samples/compound_operators.lua:9:3: value assigned to table field 't'.'a' is unused
spec/samples/compound_operators.lua:10:1: assignment uses compound operator *=
Total: 1 warning / 3 errors in 1 file
Total: 0 warnings / 3 errors in 1 file
]], get_output "spec/samples/compound_operators.lua --no-config --operators +=")
end)

it("allows to define allowed compound operators", function()
assert.equal([[Checking spec/samples/compound_operators.lua 1 warning
assert.equal([[Checking spec/samples/compound_operators.lua OK
spec/samples/compound_operators.lua:9:3: value assigned to table field 't'.'a' is unused
Total: 1 warning / 0 errors in 1 file
Total: 0 warnings / 0 errors in 1 file
]], get_output "spec/samples/compound_operators.lua --config=spec/configs/compound_operators_config.luacheckrc")
end)

Expand Down Expand Up @@ -1240,7 +1236,7 @@ Codes: true
assert.equal(([[
Checking spec/samples/argparse-0.2.0.lua 9 warnings
Checking spec/samples/compat.lua 4 warnings
Checking spec/samples/compound_operators.lua 1 warning / 4 errors
Checking spec/samples/compound_operators.lua 4 errors
Checking spec/samples/custom_std_inline_options.lua 3 warnings / 1 error
Checking spec/samples/global_inline_options.lua 3 warnings
Checking spec/samples/globals.lua 2 warnings
Expand All @@ -1258,7 +1254,7 @@ Checking spec/samples/unused_secondaries.lua 4 warnings
Checking spec/samples/utf8.lua 5 warnings
Checking spec/samples/utf8_error.lua 1 error
Total: 77 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 @@ -1267,7 +1263,7 @@ Total: 77 warnings / 9 errors in 21 files
assert.equal([[
Checking argparse-0.2.0.lua 9 warnings
Checking compat.lua 4 warnings
Checking compound_operators.lua 1 warning / 4 errors
Checking compound_operators.lua 4 errors
Checking custom_std_inline_options.lua 3 warnings / 1 error
Checking global_inline_options.lua 3 warnings
Checking globals.lua 2 warnings
Expand All @@ -1285,15 +1281,15 @@ Checking unused_secondaries.lua 4 warnings
Checking utf8.lua 5 warnings
Checking utf8_error.lua 1 error
Total: 77 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)

it("combines excluded files from config and cli", function()
assert.equal([[
Checking argparse-0.2.0.lua 9 warnings
Checking compat.lua 4 warnings
Checking compound_operators.lua 1 warning / 4 errors
Checking compound_operators.lua 4 errors
Checking custom_std_inline_options.lua 3 warnings / 1 error
Checking global_inline_options.lua 3 warnings
Checking globals.lua 2 warnings
Expand All @@ -1309,7 +1305,7 @@ Checking unused_secondaries.lua 4 warnings
Checking utf8.lua 5 warnings
Checking utf8_error.lua 1 error
Total: 69 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
32 changes: 31 additions & 1 deletion src/luacheck/stages/check_table_fields.lua
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,36 @@ function ClosureState:handle_local_or_set_item(item)
end
end

function ClosureState:handle_op_set(item)
self:check_for_function_calls(item.node)

-- By assumption, OpSet only supports a single item on the lhs/rhs
local lhs_node = item.lhs[1]
local rhs_node = item.rhs[1]

-- Only way OpSet can be relevant to tables is $table[key] += val or the like
if lhs_node.tag == "Index" then
local base_node, key_node = lhs_node[1], lhs_node[2]

-- Always accesses the lhs before writing to it
self:detect_accesses({lhs_node})

-- Case: $var[$existing_table[key]] = value
-- Need to pass in a new array rather than using lhs_node, because that would
-- mark the base *set* as also being an access
self:detect_accesses({key_node})

-- Deliberately don't continue down indexes- $table[key1][key2] isn't a new set of key1
if base_node.tag == "Id" then
-- Might not have a var if it's a global
local lhs_table_name = base_node.var and base_node.var.name
if self.current_tables[lhs_table_name] then
self:set_key(lhs_table_name, key_node, rhs_node, false)
end
end
end
end

function ClosureState:handle_eval(item)
self:check_for_function_calls(item.node)
self:detect_accesses({item.node})
Expand All @@ -438,7 +468,7 @@ local item_callbacks = {
Eval = ClosureState.handle_eval,
Local = ClosureState.handle_local_or_set_item,
Set = ClosureState.handle_local_or_set_item,
OpSet = ClosureState.handle_local_or_set_item,
OpSet = ClosureState.handle_op_set,
}

-- Steps through the closure one item at a time
Expand Down

0 comments on commit af4ca59

Please sign in to comment.