From 9b9764d6290e900c7df4e09b1d6721d7c6da9d03 Mon Sep 17 00:00:00 2001 From: Alex Richard Date: Mon, 5 Jun 2023 10:06:07 -0700 Subject: [PATCH 1/3] Bugfixes for the compound operators: (1) makes (almost) everywhere that checks against Set also check against OpSet, (2) makes OpSet have the same format (lhs and rhs are tables of assignments, same over node order with the operator on the end) as Set so that it's usable where Set is used --- src/luacheck/parser.lua | 2 +- .../stages/detect_compound_operators.lua | 2 +- src/luacheck/stages/detect_globals.lua | 2 +- src/luacheck/stages/linearize.lua | 29 +++++++++++-------- src/luacheck/stages/unwrap_parens.lua | 4 +-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/luacheck/parser.lua b/src/luacheck/parser.lua index 870a3de0..45271e13 100644 --- a/src/luacheck/parser.lua +++ b/src/luacheck/parser.lua @@ -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, "=") diff --git a/src/luacheck/stages/detect_compound_operators.lua b/src/luacheck/stages/detect_compound_operators.lua index 460306d0..279f6651 100644 --- a/src/luacheck/stages/detect_compound_operators.lua +++ b/src/luacheck/stages/detect_compound_operators.lua @@ -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 diff --git a/src/luacheck/stages/detect_globals.lua b/src/luacheck/stages/detect_globals.lua index 59ed41b0..b8b3d164 100644 --- a/src/luacheck/stages/detect_globals.lua +++ b/src/luacheck/stages/detect_globals.lua @@ -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 diff --git a/src/luacheck/stages/linearize.lua b/src/luacheck/stages/linearize.lua index 350ed88e..9d4511f9 100644 --- a/src/luacheck/stages/linearize.lua +++ b/src/luacheck/stages/linearize.lua @@ -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 = {}, @@ -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) @@ -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" @@ -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) diff --git a/src/luacheck/stages/unwrap_parens.lua b/src/luacheck/stages/unwrap_parens.lua index d17aeb49..2df055d7 100644 --- a/src/luacheck/stages/unwrap_parens.lua +++ b/src/luacheck/stages/unwrap_parens.lua @@ -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 @@ -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]] }) From bb9e7f64df507c175a18f4cfc43197f1795027a9 Mon Sep 17 00:00:00 2001 From: Alex Richard Date: Mon, 5 Jun 2023 10:09:45 -0700 Subject: [PATCH 2/3] Fix circular reference detection to note that all OpSets are circular references --- src/luacheck/stages/resolve_locals.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/luacheck/stages/resolve_locals.lua b/src/luacheck/stages/resolve_locals.lua index b5623da9..b47f0f93 100644 --- a/src/luacheck/stages/resolve_locals.lua +++ b/src/luacheck/stages/resolve_locals.lua @@ -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 From a8de4bcdc182afcfacf7c5b6a3b85d959c93df76 Mon Sep 17 00:00:00 2001 From: Alex Richard Date: Mon, 5 Jun 2023 10:45:07 -0700 Subject: [PATCH 3/3] Adds testing for the function upvalue case --- spec/cli_spec.lua | 8 ++++---- spec/samples/compound_operators.lua | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/spec/cli_spec.lua b/spec/cli_spec.lua index 9f47635e..40879e58 100644 --- a/spec/cli_spec.lua +++ b/spec/cli_spec.lua @@ -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") @@ -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 +=") diff --git a/spec/samples/compound_operators.lua b/spec/samples/compound_operators.lua index 42e6dedc..84753e45 100644 --- a/spec/samples/compound_operators.lua +++ b/spec/samples/compound_operators.lua @@ -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