Skip to content

Commit

Permalink
Assignment to global regex match data is not allowed (#12714)
Browse files Browse the repository at this point in the history
  • Loading branch information
caspiano committed Nov 11, 2022
1 parent 4d0d092 commit 3334369
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 25 deletions.
8 changes: 4 additions & 4 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2754,15 +2754,15 @@ module Crystal
end
end

describe "multiassign methods" do
multiassign_node = MultiAssign.new(["foo".var, "bar".var] of ASTNode, [2.int32, "a".string] of ASTNode)
describe "multi_assign methods" do
multi_assign_node = MultiAssign.new(["foo".var, "bar".var] of ASTNode, [2.int32, "a".string] of ASTNode)

it "executes targets" do
assert_macro %({{x.targets}}), %([foo, bar]), {x: multiassign_node}
assert_macro %({{x.targets}}), %([foo, bar]), {x: multi_assign_node}
end

it "executes values" do
assert_macro %({{x.values}}), %([2, "a"]), {x: multiassign_node}
assert_macro %({{x.values}}), %([2, "a"]), {x: multi_assign_node}
end
end

Expand Down
36 changes: 21 additions & 15 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1208,16 +1208,27 @@ module Crystal
it_parses "1.=~(2)", Call.new(1.int32, "=~", 2.int32)
it_parses "def =~; end", Def.new("=~", [] of Arg)

it_parses "$~", Global.new("$~")
it_parses "$~.foo", Call.new(Global.new("$~"), "foo")
it_parses "$0", Call.new(Global.new("$~"), "[]", 0.int32)
it_parses "$1", Call.new(Global.new("$~"), "[]", 1.int32)
it_parses "$1?", Call.new(Global.new("$~"), "[]?", 1.int32)
it_parses "foo $1", Call.new(nil, "foo", Call.new(Global.new("$~"), "[]", 1.int32))
it_parses "$~ = 1", Assign.new("$~".var, 1.int32)

assert_syntax_error "$2147483648"
assert_syntax_error "$99999999999999999999999?", "Index $99999999999999999999999 doesn't fit in an Int32"
describe "global regex match data" do
it_parses "$~", Global.new("$~")
it_parses "$~.foo", Call.new(Global.new("$~"), "foo")
it_parses "$0", Call.new(Global.new("$~"), "[]", 0.int32)
it_parses "$1", Call.new(Global.new("$~"), "[]", 1.int32)
it_parses "$1?", Call.new(Global.new("$~"), "[]?", 1.int32)
it_parses "foo $1", Call.new(nil, "foo", Call.new(Global.new("$~"), "[]", 1.int32))
it_parses "$~ = 1", Assign.new("$~".var, 1.int32)

assert_syntax_error "$0 = 1", "global match data cannot be assigned to"
assert_syntax_error "$0, $1 = [1, 2]", "global match data cannot be assigned to"
assert_syntax_error "$0, a = {1, 2}", "global match data cannot be assigned to"

assert_syntax_error "$2147483648"
assert_syntax_error "$99999999999999999999999?", "Index $99999999999999999999999 doesn't fit in an Int32"

it_parses "$?", Global.new("$?")
it_parses "$?.foo", Call.new(Global.new("$?"), "foo")
it_parses "foo $?", Call.new(nil, "foo", Global.new("$?"))
it_parses "$? = 1", Assign.new("$?".var, 1.int32)
end

it_parses "foo /a/", Call.new(nil, "foo", regex("a"))
it_parses "foo(/a/)", Call.new(nil, "foo", regex("a"))
Expand All @@ -1229,11 +1240,6 @@ module Crystal
it_parses "foo a, / /", Call.new(nil, "foo", ["a".call, regex(" ")] of ASTNode)
it_parses "foo /;/", Call.new(nil, "foo", regex(";"))

it_parses "$?", Global.new("$?")
it_parses "$?.foo", Call.new(Global.new("$?"), "foo")
it_parses "foo $?", Call.new(nil, "foo", Global.new("$?"))
it_parses "$? = 1", Assign.new("$?".var, 1.int32)

it_parses "foo out x; x", [Call.new(nil, "foo", Out.new("x".var)), "x".var]
it_parses "foo(out x); x", [Call.new(nil, "foo", Out.new("x".var)), "x".var]
it_parses "foo out @x; @x", [Call.new(nil, "foo", Out.new("@x".instance_var)), "@x".instance_var]
Expand Down
26 changes: 20 additions & 6 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -199,21 +199,21 @@ module Crystal
unexpected_token
end

targets = exps[0...assign_index].map { |exp| multiassign_left_hand(exp) }
targets = exps[0...assign_index].map { |exp| multi_assign_left_hand(exp) }

assign = exps[assign_index]
values = [] of ASTNode

case assign
when Assign
targets << multiassign_left_hand(assign.target)
targets << multi_assign_left_hand(assign.target)
values << assign.value
when Call
assign.name = assign.name.byte_slice(0, assign.name.bytesize - 1)
targets << assign
values << assign.args.pop
else
raise "BUG: multiassign index expression can only be Assign or Call"
raise "BUG: multi_assign index expression can only be Assign or Call"
end

if lhs_splat_index
Expand Down Expand Up @@ -259,14 +259,24 @@ module Crystal
end
end

def multiassign_left_hand(exp)
def multi_assign_left_hand(exp)
if exp.is_a?(Path)
raise "can't assign to constant in multiple assignment", exp.location.not_nil!
end

if exp.is_a?(Call) && !exp.obj && exp.args.empty?
exp = Var.new(exp.name).at(exp)
if exp.is_a?(Call)
case obj = exp.obj
when Nil
if exp.args.empty?
exp = Var.new(exp.name).at(exp)
end
when Global
if obj.name == "$~" && exp.name == "[]"
raise "global match data cannot be assigned to", obj.location.not_nil!
end
end
end

if exp.is_a?(Var)
if exp.name == "self"
raise "can't change the value of self", exp.location.not_nil!
Expand Down Expand Up @@ -995,6 +1005,10 @@ module Crystal
node_and_next_token Global.new(var.name).at(location)
end
when .global_match_data_index?
if peek_ahead { next_token_skip_space; @token.type.op_eq? }
raise "global match data cannot be assigned to"
end

value = @token.value.to_s
if value_prefix = value.rchop? '?'
method = "[]?"
Expand Down

0 comments on commit 3334369

Please sign in to comment.