diff --git a/spec/compiler/formatter/formatter_spec.cr b/spec/compiler/formatter/formatter_spec.cr index f43b912721f0..98df703812d3 100644 --- a/spec/compiler/formatter/formatter_spec.cr +++ b/spec/compiler/formatter/formatter_spec.cr @@ -727,13 +727,19 @@ describe Crystal::Formatter do assert_format "lib Bar\n enum Foo\n A\n end\nend" assert_format "lib Bar\n enum Foo\n A = 1\n end\nend" - assert_format "->foo=" - assert_format "foo = 1\n->foo.bar" - assert_format "foo = 1\n->foo.bar=" + %w(foo foo= foo? foo!).each do |method| + assert_format "->#{method}" + assert_format "foo = 1\n->foo.#{method}" + assert_format "->Foo.#{method}" + assert_format "->@foo.#{method}" + assert_format "->@@foo.#{method}" + end + assert_format "foo = 1\n->foo.bar(Int32)" assert_format "foo = 1\n->foo.bar(Int32*)" assert_format "foo = 1\n->foo.bar=(Int32)" assert_format "foo = 1\n->foo.[](Int32)" + assert_format "foo = 1\n->foo.[]=(Int32)" assert_format "->{ x }" assert_format "->{\nx\n}", "->{\n x\n}" assert_format "->do\nx\nend", "->do\n x\nend" @@ -742,9 +748,6 @@ describe Crystal::Formatter do assert_format "->( x , y ) { x }", "->(x, y) { x }" assert_format "->( x : Int32 , y ) { x }", "->(x : Int32, y) { x }" - assert_format "->@foo.foo" - assert_format "->@@foo.foo" - {:+, :-, :*, :/, :^, :>>, :<<, :|, :&, :&+, :&-, :&*, :&**}.each do |sym| assert_format ":#{sym}" end diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index b2e97a8e9f64..fd4581d46796 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -1272,18 +1272,20 @@ module Crystal it_parses "x = 1; ->{ x }", [Assign.new("x".var, 1.int32), ProcLiteral.new(Def.new("->", body: "x".var))] it_parses "f ->{ a do\n end\n }", Call.new(nil, "f", ProcLiteral.new(Def.new("->", body: Call.new(nil, "a", block: Block.new)))) - it_parses "->foo", ProcPointer.new(nil, "foo") - it_parses "->Foo.foo", ProcPointer.new("Foo".path, "foo") + %w(foo foo= foo? foo!).each do |method| + it_parses "->#{method}", ProcPointer.new(nil, method) + it_parses "foo = 1; ->foo.#{method}", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, method)] + it_parses "->Foo.#{method}", ProcPointer.new("Foo".path, method) + it_parses "->@foo.#{method}", ProcPointer.new("@foo".instance_var, method) + it_parses "->@@foo.#{method}", ProcPointer.new("@@foo".class_var, method) + end + it_parses "->Foo::Bar::Baz.foo", ProcPointer.new(["Foo", "Bar", "Baz"].path, "foo") it_parses "->foo(Int32, Float64)", ProcPointer.new(nil, "foo", ["Int32".path, "Float64".path] of ASTNode) it_parses "foo = 1; ->foo.bar(Int32)", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, "bar", ["Int32".path] of ASTNode)] it_parses "->foo(Void*)", ProcPointer.new(nil, "foo", ["Void".path.pointer_of] of ASTNode) it_parses "call ->foo", Call.new(nil, "call", ProcPointer.new(nil, "foo")) it_parses "[] of ->\n", ArrayLiteral.new(of: ProcNotation.new) - it_parses "->foo=", ProcPointer.new(nil, "foo=") - it_parses "foo = 1; ->foo.foo=", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, "foo=")] - it_parses "->@foo.foo", [ProcPointer.new("@foo".instance_var, "foo")] - it_parses "->@@foo.foo", [ProcPointer.new("@@foo".class_var, "foo")] it_parses "foo &->bar", Call.new(nil, "foo", block_arg: ProcPointer.new(nil, "bar")) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 32e0a6a65cd2..156ad25ad4ac 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1886,45 +1886,34 @@ module Crystal case @token.type when :IDENT name = @token.value.to_s - next_token - if @token.type == :"=" + if consume_def_equals_sign_skip_space name = "#{name}=" - next_token_skip_space - else - skip_space - if @token.type == :"." - second_name = consume_def_or_macro_name - if name != "self" && !@def_vars.last.includes?(name) - raise "undefined variable '#{name}'", location.line_number, location.column_number - end - obj = Var.new(name) - name = second_name - next_token - if @token.type == :"=" - name = "#{name}=" - next_token_skip_space - else - skip_space - end + elsif @token.type == :"." + if name != "self" && !@def_vars.last.includes?(name) + raise "undefined variable '#{name}'", location.line_number, location.column_number end + obj = Var.new(name) + + name = consume_def_or_macro_name + name = "#{name}=" if consume_def_equals_sign_skip_space end when :CONST obj = parse_generic check :"." name = consume_def_or_macro_name - next_token_skip_space + name = "#{name}=" if consume_def_equals_sign_skip_space when :INSTANCE_VAR obj = InstanceVar.new(@token.value.to_s) next_token_skip_space check :"." name = consume_def_or_macro_name - next_token_skip_space + name = "#{name}=" if consume_def_equals_sign_skip_space when :CLASS_VAR obj = ClassVar.new(@token.value.to_s) next_token_skip_space check :"." name = consume_def_or_macro_name - next_token_skip_space + name = "#{name}=" if consume_def_equals_sign_skip_space else unexpected_token end @@ -3002,12 +2991,7 @@ module Crystal raise "macro can't have a receiver" when :IDENT check_valid_def_name - next_token - if @token.type == :"=" - name += '=' - next_token - end - skip_space + name = "#{name}=" if consume_def_equals_sign_skip_space else check_valid_def_op_name next_token_skip_space @@ -3440,14 +3424,7 @@ module Crystal elsif @token.type == :IDENT check_valid_def_name name = @token.value.to_s - - next_token - if @token.type == :"=" - name = "#{name}=" - next_token_skip_space - else - skip_space - end + name = "#{name}=" if consume_def_equals_sign_skip_space else check_valid_def_op_name name = @token.type.to_s @@ -3474,13 +3451,7 @@ module Crystal name = @token.value.to_s name_location = @token.location - next_token - if @token.type == :"=" - name = "#{name}=" - next_token_skip_space - else - skip_space - end + name = "#{name}=" if consume_def_equals_sign_skip_space else check DefOrMacroCheck2 check_valid_def_op_name @@ -5976,6 +5947,17 @@ module Crystal @token.to_s end + def consume_def_equals_sign_skip_space + next_token + if @token.type == :"=" + next_token_skip_space + true + else + skip_space + false + end + end + def push_def @def_vars.push(Set(String).new) end