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

Compound bugfix #96

Merged
merged 3 commits into from
Jun 5, 2023
Merged
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
8 changes: 4 additions & 4 deletions spec/cli_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ Total: 5 warnings / 0 errors in 1 file

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:4:1: assignment uses compound operator *=
spec/samples/compound_operators.lua:5:1: assignment uses compound operator /=
spec/samples/compound_operators.lua:5:2: assignment uses compound operator /=
spec/samples/compound_operators.lua:10:1: assignment uses compound operator *=

Total: 0 warnings / 4 errors in 1 file
]], get_output "spec/samples/compound_operators.lua --no-config")
Expand All @@ -311,8 +311,8 @@ Total: 0 warnings / 4 errors in 1 file
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:4:1: assignment uses compound operator *=
spec/samples/compound_operators.lua:5:1: assignment uses compound operator /=
spec/samples/compound_operators.lua:5:2: assignment uses compound operator /=
spec/samples/compound_operators.lua:10:1: assignment uses compound operator *=

Total: 0 warnings / 3 errors in 1 file
]], get_output "spec/samples/compound_operators.lua --no-config --operators +=")
Expand Down
11 changes: 8 additions & 3 deletions spec/samples/compound_operators.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
local i = 0
i += 10
i -= 5
i *= 2
i /= 5
return i
local function func()
i /= 5
end
func()
local t = {}
t.a = i
t.a *= 2
return t
2 changes: 1 addition & 1 deletion src/luacheck/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ local function parse_expression_statement(state)
parse_error(state, "compound assignment not allowed on tuples near " .. compound_operator .. "=")
end

return new_inner_node(start_range, rhs[1], "OpSet", {compound_operator, lhs[1], rhs[1]})
return new_inner_node(start_range, rhs[1], "OpSet", {lhs, rhs, compound_operator})
else
-- This is an assignment in the form `lhs = rhs`.
check_and_skip_token(state, "=")
Expand Down
2 changes: 1 addition & 1 deletion src/luacheck/stages/detect_compound_operators.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ local reverse_compound_operators = {
}

local function check_node(chstate, node)
local operator = reverse_compound_operators[node[1]]
local operator = reverse_compound_operators[node[3]]
chstate:warn_range("033", node, {operator = operator})
end

Expand Down
2 changes: 1 addition & 1 deletion src/luacheck/stages/detect_globals.lua
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ local function detect_globals_in_line(chstate, line)
if item.rhs then
detect_in_nodes(chstate, item, item.rhs, is_top_line)
end
elseif item.tag == "Set" then
elseif item.tag == "Set" or item.tag == "OpSet" then
detect_in_nodes(chstate, item, item.lhs, is_top_line, true)
detect_in_nodes(chstate, item, item.rhs, is_top_line)
end
Expand Down
29 changes: 17 additions & 12 deletions src/luacheck/stages/linearize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ local function new_opset_item(node)
return {
tag = "OpSet",
node = node,
lhs = node[2],
rhs = node[3],
lhs = node[1],
rhs = node[2],
accesses = {},
mutations = {},
used_values = {},
Expand Down Expand Up @@ -530,18 +530,19 @@ end

function LinState:emit_stmt_OpSet(node)
local item = new_opset_item(node)
self:scan_expr(item, node[3])
self:scan_exprs(item, node[2])

local lhs = node[2]
if lhs.tag == "Id" then
local var = self:check_var(lhs)
for _, expr in ipairs(node[1]) do
if expr.tag == "Id" then
local var = self:check_var(expr)

if var then
self:register_upvalue_action(item, var, "set_upvalues")
if var then
self:register_upvalue_action(item, var, "set_upvalues")
end
else
assert(expr.tag == "Index")
self:scan_lhs_index(item, expr)
end
else
assert(lhs.tag == "Index")
self:scan_lhs_index(item, lhs)
end

self:emit(item)
Expand Down Expand Up @@ -650,7 +651,7 @@ function LinState:register_set_variables()
local line = self.lines.top

for _, item in ipairs(line.items) do
if item.tag == "Local" or item.tag == "Set" then
if item.tag == "Local" or item.tag == "Set" or item.tag == "OpSet" then
item.set_variables = {}

local is_init = item.tag == "Local"
Expand All @@ -674,6 +675,10 @@ function LinState:register_set_variables()
local value

if node.var then
-- OpSet also accesses
if item.tag == "OpSet" then
self:mark_access(item, node)
end
value = new_value(node, item.rhs and item.rhs[i] or unpacking_item, item, is_init)
item.set_variables[node.var] = value
table.insert(node.var.values, value)
Expand Down
11 changes: 8 additions & 3 deletions src/luacheck/stages/resolve_locals.lua
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,24 @@ local function contains_call(node)
end

local function is_circular_reference(item, var)
-- OpSet is circular by definition
if not (item.tag == "Set" or item.tag == "Local") then
return false
end

-- No support for matching multiple assignment to the specific assignment
if not (item.tag == "Set" or item.tag == "Local" or item.tag == "OpSet") then
if not item.lhs or #item.lhs ~= 1 or not item.rhs or #item.rhs ~= 1 then
return false
end

-- Case t[t.function()] = t.func()
-- Functions can have side-effects, so this isn't purely circular
local right_assignment = item.tag == "OpSet" and item.rhs or item.rhs[1]
local right_assignment = item.rhs[1]
if contains_call(right_assignment) then
return false
end

local left_assignment = item.tag == "OpSet" and item.lhs or item.lhs[1]
local left_assignment = item.lhs[1]
if contains_call(left_assignment) then
return false
end
Expand Down
4 changes: 2 additions & 2 deletions src/luacheck/stages/unwrap_parens.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ local function handle_nodes(chstate, nodes, list_start)
if node[2] then
handle_nodes(chstate, node[2])
end
elseif tag == "Set" then
elseif tag == "Set" or tag == "OpSet" then
handle_nodes(chstate, node[1])
handle_nodes(chstate, node[2], 1)
else
Expand All @@ -67,7 +67,7 @@ local function handle_nodes(chstate, nodes, list_start)

-- warn that not (x == y) can become x ~= y
if tag == "Op" and node[1] == "not" and node[2].tag == "Op" and relational_operators[node[2][1]] then
chstate:warn_range("581", node, {
chstate:warn_range("581", node, {
operator = relational_operators[node[2][1]],
replacement_operator = replacements[node[2][1]]
})
Expand Down