diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index b5e0a0301fc6..e44125f342fa 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -1919,6 +1919,16 @@ module Crystal assert_syntax_error "{1, ->do\n|x| x\end }", "unexpected token: \"|\", proc literals specify their parameters like this: ->(x : Type) { ... }" assert_syntax_error "{1, ->{ |_| x } }", "unexpected token: \"|\", proc literals specify their parameters like this: ->(param : Type) { ... }" + # #2874 + assert_syntax_error "A = B = 1", "dynamic constant assignment" + assert_syntax_error "A = (B = 1)", "dynamic constant assignment" + assert_syntax_error "A = foo(B = 1)", "dynamic constant assignment" + assert_syntax_error "A = foo { B = 1 }", "dynamic constant assignment" + assert_syntax_error "A = begin; B = 1; end", "dynamic constant assignment" + assert_syntax_error "A = begin; 1; rescue; B = 1; end", "dynamic constant assignment" + assert_syntax_error "A = begin; 1; rescue; 1; else; B = 1; end", "dynamic constant assignment" + assert_syntax_error "A = begin; 1; ensure; B = 1; end", "dynamic constant assignment" + assert_syntax_error "1 while 3", "trailing `while` is not supported" assert_syntax_error "1 until 3", "trailing `until` is not supported" assert_syntax_error "x++", "postfix increment is not supported, use `exp += 1`" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index ce3a1cc9b38d..701dba2246ca 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -36,6 +36,7 @@ module Crystal @def_nest = 0 @fun_nest = 0 @type_nest = 0 + @is_constant_assignment = false # Keeps track of current call args starting locations, # so if we parse a type declaration exactly at those points we @@ -370,10 +371,12 @@ module Crystal else break unless can_be_assigned?(atomic) - if atomic.is_a?(Path) && (inside_def? || inside_fun?) + if atomic.is_a?(Path) && (inside_def? || inside_fun? || @is_constant_assignment) raise "dynamic constant assignment. Constants can only be declared at the top level or inside other types." end + @is_constant_assignment = true if atomic.is_a?(Path) + if atomic.is_a?(Var) && atomic.name == "self" raise "can't change the value of self", location end @@ -422,6 +425,8 @@ module Crystal end end + @is_constant_assignment = false if atomic.is_a?(Path) + push_var atomic atomic = Assign.new(atomic, atomic_value).at(location)