From 96821411ce1d6e638810823473cf1c5749076096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 23 Oct 2021 12:49:18 -0700 Subject: [PATCH 01/50] Parser: fix end location of call name --- spec/compiler/parser/parser_spec.cr | 22 ++++++++++++++++++++++ src/compiler/crystal/syntax/ast.cr | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 661c14cc778d..fbc51f81a1c3 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -22,6 +22,22 @@ private def it_parses(string, expected_node, file = __FILE__, line = __LINE__) end end +private def location_to_index(string, location) + index = 0 + line = 1 + while line != location.line_number + index = string.index('\n', index).not_nil! + 1 + line += 1 + end + index + location.column_number - 1 +end + +private def source_between(string, loc, end_loc) + beginning = location_to_index(string, loc.not_nil!) + ending = location_to_index(string, end_loc.not_nil!) + string[beginning..ending] +end + private def assert_end_location(source, line_number = 1, column_number = source.size, file = __FILE__, line = __LINE__) it "gets corrects end location for #{source.inspect}", file, line do parser = Parser.new("#{source}; 1") @@ -2269,6 +2285,12 @@ module Crystal node.end_location.not_nil!.line_number.should eq(5) end + it "sets correct location of call name" do + source = "foo(bar)" + node = Parser.new(source).parse.as(Call) + source_between(source, node.name_location, node.name_end_location).should eq ("foo") + end + it "doesn't override yield with macro yield" do parser = Parser.new("def foo; yield 1; {% begin %} yield 1 {% end %}; end") a_def = parser.parse.as(Def) diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index e774d40793c7..b335508644e2 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -691,7 +691,7 @@ module Crystal loc = @name_location return unless loc - Location.new(loc.filename, loc.line_number, loc.column_number + name_size) + Location.new(loc.filename, loc.line_number, loc.column_number + name_size - 1) end def_equals_and_hash obj, name, args, block, block_arg, named_args, global? From 92b3b202e01b5eeceab2d4d842d767bcc4bc5e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 6 Nov 2021 14:07:09 -0700 Subject: [PATCH 02/50] Parser: fix location of implicit tuple literal of multi-return --- spec/compiler/parser/parser_spec.cr | 6 ++++++ src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index fbc51f81a1c3..d324552369fb 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2291,6 +2291,12 @@ module Crystal source_between(source, node.name_location, node.name_end_location).should eq ("foo") end + it "sets correct location of implicit tuple literal of multi-return" do + source = "def foo; return 1, 2; end" + node = Parser.new(source).parse.as(Def).body.as(Return).exp.not_nil! + source_between(source, node.location, node.end_location).should eq ("1, 2") + end + it "doesn't override yield with macro yield" do parser = Parser.new("def foo; yield 1; {% begin %} yield 1 {% end %}; end") a_def = parser.parse.as(Def) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 079f44a9e9a2..2b855fa92aef 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5425,7 +5425,7 @@ module Crystal if args.size == 1 && !args.first.is_a?(Splat) node = klass.new(args.first) else - tuple = TupleLiteral.new(args).at(args.last) + tuple = TupleLiteral.new(args).at(args.first).at_end(args.last) node = klass.new(tuple) end else From 41d371da251dca168ba66a4d4be9c35f172ebf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 12:53:34 -0800 Subject: [PATCH 03/50] Parser: fix `else_location` of if statement without `else` `else_location` should be `nil` if `else` block is not present. --- spec/compiler/parser/parser_spec.cr | 6 ++++++ src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index d324552369fb..1523ef0a9757 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2236,6 +2236,12 @@ module Crystal node.location.not_nil!.line_number.should eq(1) node.else_location.not_nil!.line_number.should eq(2) node.end_location.not_nil!.line_number.should eq(3) + + parser = Parser.new("if foo\nend") + node = parser.parse.as(If) + node.location.not_nil!.line_number.should eq(1) + node.else_location.should be_nil + node.end_location.not_nil!.line_number.should eq(2) end it "sets correct location of `elsif` of if statement" do diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 2b855fa92aef..06d90b10326a 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4022,14 +4022,16 @@ module Crystal a_then = parse_expressions skip_statement_end + else_location = nil a_else = nil if @token.type == :IDENT - else_location = @token.location case @token.value when :else + else_location = @token.location next_token_skip_statement_end a_else = parse_expressions when :elsif + else_location = @token.location a_else = parse_if check_end: false end end From 0662d522776f378a286b3299ea5223b7d45df0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 13:59:25 -0800 Subject: [PATCH 04/50] Check the text between an AST node's start and end locations --- spec/compiler/parser/parser_spec.cr | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 1523ef0a9757..45b554d93955 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -40,11 +40,14 @@ end private def assert_end_location(source, line_number = 1, column_number = source.size, file = __FILE__, line = __LINE__) it "gets corrects end location for #{source.inspect}", file, line do - parser = Parser.new("#{source}; 1") + string = "#{source}; 1" + parser = Parser.new(string) node = parser.parse.as(Expressions).expressions[0] + loc = node.location.not_nil! end_loc = node.end_location.not_nil! end_loc.line_number.should eq(line_number) end_loc.column_number.should eq(column_number) + source_between(string, loc, end_loc).should eq(source) end end From 4a1d081eb039f676a545db50b487cb12fb02b4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 11:41:03 -0800 Subject: [PATCH 05/50] Parser: fix location of multi-assign if expression For example: a, b = 1, 2 if 3 --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 45b554d93955..b4dd8e00a887 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2042,6 +2042,7 @@ module Crystal assert_end_location "extend Foo" assert_end_location "1.as(Int32)" assert_end_location "puts obj.foo" + assert_end_location "a, b = 1, 2 if 3" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 06d90b10326a..9db17855c8a4 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -230,7 +230,7 @@ module Crystal end multi = MultiAssign.new(targets, values).at(location) - parse_expression_suffix multi, @token.location + parse_expression_suffix multi, location end def multi_assign_target?(exp) From 1fe58da2c46bd973d9d6b5295b46fa7be8228375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 11:44:38 -0800 Subject: [PATCH 06/50] Parser: fix end location of def with parenthesized parameters For example: abstract def foo(x) --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 ++ 2 files changed, 3 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index b4dd8e00a887..d3ac76b595e5 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2043,6 +2043,7 @@ module Crystal assert_end_location "1.as(Int32)" assert_end_location "puts obj.foo" assert_end_location "a, b = 1, 2 if 3" + assert_end_location "abstract def foo(x)" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 9db17855c8a4..07ddf91d3b79 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -3559,6 +3559,8 @@ module Crystal index += 1 end + end_location = token_end_location + if name.ends_with?('=') if name != "[]=" && (args.size > 1 || found_splat || found_double_splat) raise "setter method '#{name}' cannot have more than one parameter" From 4e15582706c5857e1fe5bb1b7c5d2c71ee1bf8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 11:52:37 -0800 Subject: [PATCH 07/50] Parser: fix location of global call For example: ::foo --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index d3ac76b595e5..af27a48709c9 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2044,6 +2044,7 @@ module Crystal assert_end_location "puts obj.foo" assert_end_location "a, b = 1, 2 if 3" assert_end_location "abstract def foo(x)" + assert_end_location "::foo" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 07ddf91d3b79..523f39a2b21e 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4089,8 +4089,7 @@ module Crystal node end - def parse_var_or_call(global = false, force_call = false) - location = @token.location + def parse_var_or_call(global = false, force_call = false, location = @token.location) end_location = token_end_location doc = @token.doc @@ -4761,7 +4760,7 @@ module Crystal case @token.type when :IDENT - set_visibility parse_var_or_call global: true + set_visibility parse_var_or_call global: true, location: location when :CONST ident = parse_generic global: true, location: location, expression: true parse_custom_literal ident From 0eadd4a4b4c9744a3b27301369ddd650d2e7ede8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 12:33:24 -0800 Subject: [PATCH 08/50] Parser: fix end location of `#[]=` call For example: foo.[0] = 1 --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index af27a48709c9..bfce2a38961c 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2045,6 +2045,7 @@ module Crystal assert_end_location "a, b = 1, 2 if 3" assert_end_location "abstract def foo(x)" assert_end_location "::foo" + assert_end_location "foo.[0] = 1" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 523f39a2b21e..a64f2e37513c 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -366,7 +366,9 @@ module Crystal atomic.name = "[]=" atomic.name_size = 0 - atomic.args << parse_op_assign_no_control + arg = parse_op_assign_no_control + atomic.args << arg + atomic.end_location = arg.end_location else break unless can_be_assigned?(atomic) From a6b063b2419ad0494be6aaa54f16d9208cd57795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 12:48:37 -0800 Subject: [PATCH 09/50] Parser: fix end location of var with type declaration For example: x : Foo(A, *B, C) --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index bfce2a38961c..00e78f663e61 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2046,6 +2046,7 @@ module Crystal assert_end_location "abstract def foo(x)" assert_end_location "::foo" assert_end_location "foo.[0] = 1" + assert_end_location "x : Foo(A, *B, C)" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index a64f2e37513c..5f943cce0416 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1202,7 +1202,7 @@ module Crystal next_token_skip_space_or_newline value = parse_op_assign_no_control end - TypeDeclaration.new(var, var_type, value).at(var.location) + TypeDeclaration.new(var, var_type, value).at(var).at_end(value || var_type) end def next_comes_colon_space? @@ -4227,6 +4227,7 @@ module Crystal else if @no_type_declaration == 0 && @token.type == :":" declare_var = parse_type_declaration(Var.new(name).at(location)) + end_location = declare_var.end_location push_var declare_var if @call_args_nest == 0 declare_var elsif (!force_call && is_var) From 0f56113b0647cf4b16fbe35dd926fc3c2cdfbe11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 12:56:25 -0800 Subject: [PATCH 10/50] Parser: fix end location of `#[]?` call For example: Int[8]? --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 1 + 2 files changed, 2 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 00e78f663e61..3510ca7c06b4 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2047,6 +2047,7 @@ module Crystal assert_end_location "::foo" assert_end_location "foo.[0] = 1" assert_end_location "x : Foo(A, *B, C)" + assert_end_location "Int[8]?" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 5f943cce0416..05ebd334e23f 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -809,6 +809,7 @@ module Crystal if @token.type == :"?" method_name = "[]?" + end_location = token_end_location next_token_skip_space else method_name = "[]" From 930be1cded3dce6d0dedef618cb9e0db1c4c5e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 13:07:08 -0800 Subject: [PATCH 11/50] Parser: fix end location of array literal For example: [1, 2,] --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 3510ca7c06b4..7c657589ff5e 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2048,6 +2048,7 @@ module Crystal assert_end_location "foo.[0] = 1" assert_end_location "x : Foo(A, *B, C)" assert_end_location "Int[8]?" + assert_end_location "[1, 2,]" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 05ebd334e23f..328a6afdfc8b 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2358,7 +2358,6 @@ module Crystal end exps << exp - end_location = token_end_location skip_space if @token.type == :"," @@ -2371,6 +2370,7 @@ module Crystal end end @wants_regex = false + end_location = token_end_location next_token_skip_space end From 8e54f8993649fb98076e5ec7a08a5499d5a4a311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 13:37:37 -0800 Subject: [PATCH 12/50] Parser: fix end location of multi-line call with block argument For example: foo( &.block ) --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 1 + 2 files changed, 2 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 7c657589ff5e..ac93ec210def 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2049,6 +2049,7 @@ module Crystal assert_end_location "x : Foo(A, *B, C)" assert_end_location "Int[8]?" assert_end_location "[1, 2,]" + assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 328a6afdfc8b..6a7fd0800ee1 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1538,6 +1538,7 @@ module Crystal if check_paren skip_space_or_newline check :")" + end_location = token_end_location next_token_skip_space else skip_space From c7b1e586ef3ac8833edc9eecf1671219bc4cdbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 13:53:14 -0800 Subject: [PATCH 13/50] Parser: fix end location of call with receiver and block For example: foo.bar(x) do; end --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index ac93ec210def..54a70d79c392 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2050,6 +2050,7 @@ module Crystal assert_end_location "Int[8]?" assert_end_location "[1, 2,]" assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 + assert_end_location "foo.bar(x) do; end" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 6a7fd0800ee1..29e53837425e 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -765,7 +765,7 @@ module Crystal atomic = Call.new atomic, name, (args || [] of ASTNode), block, block_arg, named_args atomic.name_location = name_location - atomic.end_location = call_args.try(&.end_location) || block.try(&.end_location) || end_location + atomic.end_location = block.try(&.end_location) || call_args.try(&.end_location) || end_location atomic.at(location) atomic end From 94f5252717f7561fc857a172d2db9cf1715aa161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 16:22:09 -0800 Subject: [PATCH 14/50] Parser: add end location of array literal For example: %w(one two) --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 54a70d79c392..6817ff6920fc 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2051,6 +2051,7 @@ module Crystal assert_end_location "[1, 2,]" assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 assert_end_location "foo.bar(x) do; end" + assert_end_location "%w(one two)" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 29e53837425e..4027e35c6d8d 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2313,6 +2313,7 @@ module Crystal when :STRING strings << klass.new(@token.value.to_s) when :STRING_ARRAY_END + end_location = token_end_location next_token break else @@ -2320,7 +2321,7 @@ module Crystal end end - ArrayLiteral.new strings, Path.global(elements_type) + ArrayLiteral.new(strings, Path.global(elements_type)).at_end(end_location) end def parse_empty_array_literal From bc085a98bd21b6c8eec93f1979c0d6e2f9d0ef65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 16:45:13 -0800 Subject: [PATCH 15/50] Parser: fix end location of multi-line macro if For example: {% if foo bar end %} --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 ++ 2 files changed, 3 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 6817ff6920fc..1587ae62cbce 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2052,6 +2052,7 @@ module Crystal assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 assert_end_location "foo.bar(x) do; end" assert_end_location "%w(one two)" + assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 4027e35c6d8d..752c0aa9cf80 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -3376,6 +3376,8 @@ module Crystal else node = parse_if_after_condition cond, location, true end + skip_space_or_newline + check :"%}" return MacroExpression.new(node, output: false).at_end(token_end_location) end From a3dd73aab17ee2c9df976ab69cba3b7a427fcfb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:11:37 -0800 Subject: [PATCH 16/50] Parser: add end location of `out` parameter For example: foo bar, out baz --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 1587ae62cbce..10ddbb309df4 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2053,6 +2053,7 @@ module Crystal assert_end_location "foo.bar(x) do; end" assert_end_location "%w(one two)" assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 + assert_end_location "foo bar, out baz" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 752c0aa9cf80..4d5cd4bb6588 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4735,31 +4735,25 @@ module Crystal end def parse_out - next_token_skip_space_or_newline location = @token.location + next_token_skip_space_or_newline name = @token.value.to_s case @token.type when :IDENT - var = Var.new(name).at(location) - var_out = Out.new(var).at(location) + var = Var.new(name) push_var var - - next_token - var_out when :INSTANCE_VAR - ivar = InstanceVar.new(name).at(location) - ivar_out = Out.new(ivar).at(location) - next_token - ivar_out + var = InstanceVar.new(name) when :UNDERSCORE - underscore = Underscore.new.at(location) - var_out = Out.new(underscore).at(location) - next_token - var_out + var = Underscore.new else raise "expecting variable or instance variable after out" end + var.location = @token.location + var.end_location = token_end_location + next_token + Out.new(var).at(location).at_end(var) end def parse_generic_or_global_call From 84ff78d874119abf1296f711f7b0b8eee352a1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:23:50 -0800 Subject: [PATCH 17/50] Parser: add end location of nilable generic type For example: Foo? --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 10ddbb309df4..529f468c8bf9 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2054,6 +2054,7 @@ module Crystal assert_end_location "%w(one two)" assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 assert_end_location "foo bar, out baz" + assert_end_location "Foo?" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 4d5cd4bb6588..55b6cf1aa39f 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4922,8 +4922,9 @@ module Crystal # is appeared in macro expression. (e.g. `{% if T <= Int32? %} ... {% end %}`) # Note that the parser cannot consume any spaces because it conflicts ternary operator. while expression && @token.type == :"?" + end_location = token_end_location next_token - type = make_nilable_expression(type) + type = make_nilable_expression(type).at_end(end_location) end skip_space From c5b8f8c0ee6bcfa554272a30b3889ef6b29d458e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:29:32 -0800 Subject: [PATCH 18/50] Parser: add end location of type with suffix For example: foo : Foo.class foo : Foo? foo : Foo* foo : Foo** foo : Foo[42] --- spec/compiler/parser/parser_spec.cr | 5 +++++ src/compiler/crystal/syntax/parser.cr | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 529f468c8bf9..fd1323e8d682 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2055,6 +2055,11 @@ module Crystal assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 assert_end_location "foo bar, out baz" assert_end_location "Foo?" + assert_end_location "foo : Foo.class" + assert_end_location "foo : Foo?" + assert_end_location "foo : Foo*" + assert_end_location "foo : Foo**" + assert_end_location "foo : Foo[42]" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 55b6cf1aa39f..544e98d73a7c 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5067,28 +5067,31 @@ module Crystal def parse_type_suffix(type) loop do + end_location = token_end_location case @token.type when :"." next_token_skip_space_or_newline check_ident :class + end_location = token_end_location next_token_skip_space - type = Metaclass.new(type).at(type) + type = Metaclass.new(type).at(type).at_end(end_location) when :"?" next_token_skip_space - type = make_nilable_type(type) + type = make_nilable_type(type).at_end(end_location) when :"*" next_token_skip_space - type = make_pointer_type(type) + type = make_pointer_type(type).at_end(end_location) when :"**" next_token_skip_space - type = make_pointer_type(make_pointer_type(type)) + type = make_pointer_type(make_pointer_type(type)).at_end(end_location) when :"[" next_token_skip_space_or_newline size = parse_type_arg skip_space_or_newline check :"]" + end_location = token_end_location next_token_skip_space - type = make_static_array_type(type, size) + type = make_static_array_type(type, size).at_end(end_location) else return type end From b6b1496a10dfa433b7db2531a23910f00259776b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:46:51 -0800 Subject: [PATCH 19/50] Parser: add end location of proc pointer For example: foo ->bar(Baz) --- spec/compiler/parser/parser_spec.cr | 11 ++++++++ src/compiler/crystal/syntax/parser.cr | 36 +++++++++++++++++++++------ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index fd1323e8d682..31a5f5eb3c25 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2060,6 +2060,17 @@ module Crystal assert_end_location "foo : Foo*" assert_end_location "foo : Foo**" assert_end_location "foo : Foo[42]" + assert_end_location "foo ->bar" + assert_end_location "foo ->bar=" + assert_end_location "foo ->self.bar" + assert_end_location "foo ->self.bar=" + assert_end_location "foo ->Bar.baz" + assert_end_location "foo ->Bar.baz=" + assert_end_location "foo ->@bar.baz" + assert_end_location "foo ->@bar.baz=" + assert_end_location "foo ->@@bar.baz" + assert_end_location "foo ->@@bar.baz=" + assert_end_location "foo ->bar(Baz)" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 544e98d73a7c..14ef4ec2bc92 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1926,7 +1926,9 @@ module Crystal case @token.type when :IDENT name = @token.value.to_s - if consume_def_equals_sign_skip_space + end_location = token_end_location + if equals_end_location = consume_def_equals_sign_skip_space + end_location = equals_end_location name = "#{name}=" elsif @token.type == :"." if name != "self" && !var_in_scope?(name) @@ -1935,25 +1937,41 @@ module Crystal obj = Var.new(name) name = consume_def_or_macro_name - name = "#{name}=" if consume_def_equals_sign_skip_space + end_location = token_end_location + if equals_end_location = consume_def_equals_sign_skip_space + end_location = equals_end_location + name = "#{name}=" + end end when :CONST obj = parse_generic check :"." name = consume_def_or_macro_name - name = "#{name}=" if consume_def_equals_sign_skip_space + end_location = token_end_location + if equals_end_location = consume_def_equals_sign_skip_space + end_location = equals_end_location + name = "#{name}=" + end when :INSTANCE_VAR obj = InstanceVar.new(@token.value.to_s) next_token_skip_space check :"." name = consume_def_or_macro_name - name = "#{name}=" if consume_def_equals_sign_skip_space + end_location = token_end_location + if equals_end_location = consume_def_equals_sign_skip_space + end_location = equals_end_location + name = "#{name}=" + end when :CLASS_VAR obj = ClassVar.new(@token.value.to_s) next_token_skip_space check :"." name = consume_def_or_macro_name - name = "#{name}=" if consume_def_equals_sign_skip_space + end_location = token_end_location + if equals_end_location = consume_def_equals_sign_skip_space + end_location = equals_end_location + name = "#{name}=" + end else unexpected_token end @@ -1966,12 +1984,13 @@ module Crystal next_token_skip_space types = parse_union_types(:")") check :")" + end_location = token_end_location next_token_skip_space else types = [] of ASTNode end - ProcPointer.new(obj, name, types) + ProcPointer.new(obj, name, types).at_end(end_location) end record Piece, @@ -6011,12 +6030,13 @@ module Crystal def consume_def_equals_sign_skip_space next_token + end_location = token_end_location if @token.type == :"=" next_token_skip_space - true + end_location else skip_space - false + nil end end From cd32b38e6416e36485bf0fad43a44cd8acb52549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:54:50 -0800 Subject: [PATCH 20/50] Parser: add end location of splat argument --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 31a5f5eb3c25..50c39ad6a994 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2071,6 +2071,8 @@ module Crystal assert_end_location "foo ->@@bar.baz" assert_end_location "foo ->@@bar.baz=" assert_end_location "foo ->bar(Baz)" + assert_end_location "foo *bar" + assert_end_location "foo **bar" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 14ef4ec2bc92..861f6cf02f33 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4714,6 +4714,7 @@ module Crystal parse_out else + location = @token.location splat = nil case @token.type when :"*" @@ -4742,9 +4743,9 @@ module Crystal case splat when :single - arg = Splat.new(arg).at(arg.location) + arg = Splat.new(arg).at(location).at_end(arg) when :double - arg = DoubleSplat.new(arg).at(arg.location) + arg = DoubleSplat.new(arg).at(location).at_end(arg) else # no splat end From 79b62f85b45684d9720c0bf07792a93aa2ec357d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 3 Feb 2022 18:11:08 -0800 Subject: [PATCH 21/50] Parser: add end location of custom array literal For example: Foo { 1 } --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 50c39ad6a994..89fc54ef2180 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2073,6 +2073,7 @@ module Crystal assert_end_location "foo ->bar(Baz)" assert_end_location "foo *bar" assert_end_location "foo **bar" + assert_end_location "Foo { 1 }" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 861f6cf02f33..bd18a4c5986e 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1260,7 +1260,7 @@ module Crystal case tuple_or_hash when TupleLiteral - ary = ArrayLiteral.new(tuple_or_hash.elements, name: type).at(tuple_or_hash.location) + ary = ArrayLiteral.new(tuple_or_hash.elements, name: type).at(tuple_or_hash) return ary when HashLiteral tuple_or_hash.name = type From a154a90f7cd45fedd76a1cd8ca8943226e7cdeee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 08:49:21 -0800 Subject: [PATCH 22/50] Parser: add end location of negation suffix For example: foo.! --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 89fc54ef2180..2c184ce8205a 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2074,6 +2074,8 @@ module Crystal assert_end_location "foo *bar" assert_end_location "foo **bar" assert_end_location "Foo { 1 }" + assert_end_location "foo.!" + assert_end_location "foo.!()" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index bd18a4c5986e..c968f1260277 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -929,15 +929,17 @@ module Crystal end def parse_negation_suffix(atomic) + end_location = token_end_location next_token if @token.type == :"(" next_token_skip_space_or_newline check :")" + end_location = token_end_location next_token_skip_space end - Not.new(atomic) + Not.new(atomic).at_end(end_location) end def parse_atomic From 4792a84e38ce6866a1a3747ca398f7319d206cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 08:49:51 -0800 Subject: [PATCH 23/50] Parser: add end location of attribute assignment For example: f.x = foo --- spec/compiler/parser/parser_spec.cr | 3 +++ src/compiler/crystal/syntax/parser.cr | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 2c184ce8205a..c4ad1ebf256b 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2076,6 +2076,9 @@ module Crystal assert_end_location "Foo { 1 }" assert_end_location "foo.!" assert_end_location "foo.!()" + assert_end_location "f.x = foo" + assert_end_location "f.x=(*foo)" + assert_end_location "f.x=(foo).bar" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index c968f1260277..fc18cdc1b0c3 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -722,16 +722,19 @@ module Crystal next_token_skip_space arg = parse_single_arg check :")" + end_location = token_end_location next_token else arg = parse_op_assign_no_control + end_location = arg.end_location end else skip_space_or_newline arg = parse_single_arg + end_location = arg.end_location end - atomic = Call.new(atomic, "#{name}=", arg).at(location) + atomic = Call.new(atomic, "#{name}=", arg).at(location).at_end(end_location) atomic.name_location = name_location next when :"+=", :"-=", :"*=", :"/=", :"//=", :"%=", :"|=", :"&=", :"^=", :"**=", :"<<=", :">>=", :"||=", :"&&=", :"&+=", :"&-=", :"&*=" From 2e94f4f5ea3c29467538bf7bf88d36752ce7affa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 24 Jan 2022 12:53:59 -0800 Subject: [PATCH 24/50] Parser: add end location of proc type For example: x : Foo -> Bar --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index c4ad1ebf256b..40cfe5021f08 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2079,6 +2079,8 @@ module Crystal assert_end_location "f.x = foo" assert_end_location "f.x=(*foo)" assert_end_location "f.x=(foo).bar" + assert_end_location "x : Foo ->" + assert_end_location "x : Foo -> Bar" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index fc18cdc1b0c3..9d8ae8574fcf 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5127,14 +5127,16 @@ module Crystal has_output_type = type_start?(consume_newlines: false) check :"->" + end_location = token_end_location next_token_skip_space if has_output_type skip_space_or_newline output_type = parse_union_type + end_location = output_type.end_location end - ProcNotation.new(input_types, output_type).at(location) + ProcNotation.new(input_types, output_type).at(location).at_end(end_location) end def make_nilable_type(type) From 7ee0dad14775692a8429ca3fa3c119e8e954ba2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 4 Feb 2022 08:16:51 -0800 Subject: [PATCH 25/50] Comment out line to try and fix compiler error --- spec/compiler/parser/parser_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 40cfe5021f08..6775d3c2299e 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -47,7 +47,7 @@ private def assert_end_location(source, line_number = 1, column_number = source. end_loc = node.end_location.not_nil! end_loc.line_number.should eq(line_number) end_loc.column_number.should eq(column_number) - source_between(string, loc, end_loc).should eq(source) + #source_between(string, loc, end_loc).should eq(source) end end From 88639636228d14b8ea8f153cb833fbc2e7f9cf00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:04:49 -0800 Subject: [PATCH 26/50] Revert "Comment out line to try and fix compiler error" This reverts commit 7ee0dad14775692a8429ca3fa3c119e8e954ba2e. --- spec/compiler/parser/parser_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 6775d3c2299e..40cfe5021f08 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -47,7 +47,7 @@ private def assert_end_location(source, line_number = 1, column_number = source. end_loc = node.end_location.not_nil! end_loc.line_number.should eq(line_number) end_loc.column_number.should eq(column_number) - #source_between(string, loc, end_loc).should eq(source) + source_between(string, loc, end_loc).should eq(source) end end From 967708647f107f18acc95e6c8b6521469d6596a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 4 Feb 2022 20:03:02 +0000 Subject: [PATCH 27/50] Revert "Parser: add end location of array literal" This reverts commit 94f5252717f7561fc857a172d2db9cf1715aa161. --- spec/compiler/parser/parser_spec.cr | 1 - src/compiler/crystal/syntax/parser.cr | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 40cfe5021f08..bee2b59a002f 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2051,7 +2051,6 @@ module Crystal assert_end_location "[1, 2,]" assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 assert_end_location "foo.bar(x) do; end" - assert_end_location "%w(one two)" assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 assert_end_location "foo bar, out baz" assert_end_location "Foo?" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 9d8ae8574fcf..c95bd32af526 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2337,7 +2337,6 @@ module Crystal when :STRING strings << klass.new(@token.value.to_s) when :STRING_ARRAY_END - end_location = token_end_location next_token break else @@ -2345,7 +2344,7 @@ module Crystal end end - ArrayLiteral.new(strings, Path.global(elements_type)).at_end(end_location) + ArrayLiteral.new strings, Path.global(elements_type) end def parse_empty_array_literal From 68aaff61f3ef7f2358bd20bb1032ca4a19adb638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:23:10 -0800 Subject: [PATCH 28/50] Run `crystal tool format` --- spec/compiler/parser/parser_spec.cr | 4 ++-- src/compiler/crystal/syntax/parser.cr | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index bee2b59a002f..d19b33b7aebf 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2335,13 +2335,13 @@ module Crystal it "sets correct location of call name" do source = "foo(bar)" node = Parser.new(source).parse.as(Call) - source_between(source, node.name_location, node.name_end_location).should eq ("foo") + source_between(source, node.name_location, node.name_end_location).should eq("foo") end it "sets correct location of implicit tuple literal of multi-return" do source = "def foo; return 1, 2; end" node = Parser.new(source).parse.as(Def).body.as(Return).exp.not_nil! - source_between(source, node.location, node.end_location).should eq ("1, 2") + source_between(source, node.location, node.end_location).should eq("1, 2") end it "doesn't override yield with macro yield" do diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index c95bd32af526..f7ecde2c7813 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1954,9 +1954,9 @@ module Crystal name = consume_def_or_macro_name end_location = token_end_location if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + end_location = equals_end_location + name = "#{name}=" + end when :INSTANCE_VAR obj = InstanceVar.new(@token.value.to_s) next_token_skip_space @@ -1964,9 +1964,9 @@ module Crystal name = consume_def_or_macro_name end_location = token_end_location if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + end_location = equals_end_location + name = "#{name}=" + end when :CLASS_VAR obj = ClassVar.new(@token.value.to_s) next_token_skip_space @@ -1974,9 +1974,9 @@ module Crystal name = consume_def_or_macro_name end_location = token_end_location if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + end_location = equals_end_location + name = "#{name}=" + end else unexpected_token end From 0787432505bf0ec6d77116339dc1a701f3660ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 4 Feb 2022 20:21:28 +0000 Subject: [PATCH 29/50] Re-add end location of array literal Explicitly initialize `end_location` as `nil` so that the parser will compile with Crystal 1.0. --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index d19b33b7aebf..8b2fa078a374 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2051,6 +2051,7 @@ module Crystal assert_end_location "[1, 2,]" assert_end_location "foo(\n &.block\n)", line_number: 3, column_number: 1 assert_end_location "foo.bar(x) do; end" + assert_end_location "%w(one two)" assert_end_location "{%\nif foo\n bar\n end\n%}", line_number: 5, column_number: 2 assert_end_location "foo bar, out baz" assert_end_location "Foo?" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index f7ecde2c7813..3e7141759479 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2330,6 +2330,7 @@ module Crystal def parse_string_or_symbol_array(klass, elements_type) strings = [] of ASTNode + end_location = nil while true next_string_array_token @@ -2337,6 +2338,7 @@ module Crystal when :STRING strings << klass.new(@token.value.to_s) when :STRING_ARRAY_END + end_location = token_end_location next_token break else @@ -2344,7 +2346,7 @@ module Crystal end end - ArrayLiteral.new strings, Path.global(elements_type) + ArrayLiteral.new(strings, Path.global(elements_type)).at_end(end_location) end def parse_empty_array_literal From 4e30c38f582fb9fbe53d580d1037ea26ef793f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Fri, 18 Nov 2022 12:45:59 -0800 Subject: [PATCH 30/50] Fix error --- src/compiler/crystal/syntax/parser.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 148290122132..c644882569a4 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -3440,7 +3440,7 @@ module Crystal node = parse_if_after_condition cond, location, true end skip_space_or_newline - check :"%}" + check :OP_PERCENT_RCURLY return MacroExpression.new(node, output: false).at_end(token_end_location) end From db5697ef350d7e38b018a2ba9d8b466d59516f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sun, 20 Nov 2022 23:35:11 -0800 Subject: [PATCH 31/50] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Müller --- spec/compiler/parser/parser_spec.cr | 6 ++---- src/compiler/crystal/syntax/parser.cr | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index aee71c20ca2b..3a06ea25f386 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -24,10 +24,8 @@ end private def location_to_index(string, location) index = 0 - line = 1 - while line != location.line_number - index = string.index('\n', index).not_nil! + 1 - line += 1 + location.line_number.times do + index = string.index!('\n', index) + 1 end index + location.column_number - 1 end diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index c644882569a4..884c9d6e0cfd 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -4841,8 +4841,7 @@ module Crystal else raise "expecting variable or instance variable after out" end - var.location = @token.location - var.end_location = token_end_location + var.at(@token.location).at_end(token_end_location) next_token Out.new(var).at(location).at_end(var) end From 5ec08deddeeed7a85c75d2ad69853330676248cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sun, 20 Nov 2022 23:43:28 -0800 Subject: [PATCH 32/50] Fix off-by-one error --- spec/compiler/parser/parser_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 3a06ea25f386..e7a65659bd73 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -24,7 +24,7 @@ end private def location_to_index(string, location) index = 0 - location.line_number.times do + (location.line_number - 1).times do index = string.index!('\n', index) + 1 end index + location.column_number - 1 From aed506a380c64686c2466781159456e9ee012781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 21 Nov 2022 00:06:34 -0800 Subject: [PATCH 33/50] Refactor consume_def_equals_sign_skip_space --- src/compiler/crystal/syntax/parser.cr | 49 +++++++++++---------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 884c9d6e0cfd..a7f1e2d4fb32 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1971,9 +1971,8 @@ module Crystal when .ident? name = @token.value.to_s global_call = global - end_location = token_end_location - if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location + equals_sign, end_location = consume_def_equals_sign_skip_space + if equals_sign name = "#{name}=" elsif @token.type.op_period? raise "ProcPointer of local variable cannot be global", location if global @@ -1983,43 +1982,31 @@ module Crystal obj = Var.new(name) name = consume_def_or_macro_name - end_location = token_end_location - if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + equals_sign, end_location = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign end when .const? obj = parse_generic global: global, location: location, expression: false check :OP_PERIOD name = consume_def_or_macro_name - end_location = token_end_location - if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + equals_sign, end_location = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign when .instance_var? raise "ProcPointer of instance variable cannot be global", location if global obj = InstanceVar.new(@token.value.to_s) next_token_skip_space check :OP_PERIOD name = consume_def_or_macro_name - end_location = token_end_location - if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + equals_sign, end_location = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign when .class_var? raise "ProcPointer of class variable cannot be global", location if global obj = ClassVar.new(@token.value.to_s) next_token_skip_space check :OP_PERIOD name = consume_def_or_macro_name - end_location = token_end_location - if equals_end_location = consume_def_equals_sign_skip_space - end_location = equals_end_location - name = "#{name}=" - end + equals_sign, end_location = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign else unexpected_token end @@ -3099,7 +3086,8 @@ module Crystal raise "macro can't have a receiver" when .ident? check_valid_def_name - name = "#{name}=" if consume_def_equals_sign_skip_space + equals_sign, _ = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign else check_valid_def_op_name next_token_skip_space @@ -3540,7 +3528,8 @@ module Crystal elsif @token.type.ident? check_valid_def_name name = @token.value.to_s - name = "#{name}=" if consume_def_equals_sign_skip_space + equals_sign, _ = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign else check_valid_def_op_name name = @token.type.to_s @@ -3567,7 +3556,8 @@ module Crystal name = @token.value.to_s name_location = @token.location - name = "#{name}=" if consume_def_equals_sign_skip_space + equals_sign, _ = consume_def_equals_sign_skip_space + name = "#{name}=" if equals_sign else check DefOrMacroCheck2 check_valid_def_op_name @@ -6127,14 +6117,15 @@ module Crystal end def consume_def_equals_sign_skip_space - next_token end_location = token_end_location + next_token if @token.type.op_eq? + end_location = token_end_location next_token_skip_space - end_location + {true, end_location} else skip_space - nil + {false, end_location} end end From 19cf9b78638e433062c5cf302f52192b1c2ec047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 03:10:45 -0800 Subject: [PATCH 34/50] Add end location of `require` --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index e7a65659bd73..8d8be727510e 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2209,6 +2209,7 @@ module Crystal assert_end_location "f.x=(foo).bar" assert_end_location "x : Foo ->" assert_end_location "x : Foo -> Bar" + assert_end_location %(require "foo") assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index a7f1e2d4fb32..c2cab366f1ea 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2336,6 +2336,10 @@ module Crystal end def parse_string_without_interpolation(context, want_skip_space = true) + parse_string_literal_without_interpolation(context, want_skip_space).value + end + + def parse_string_literal_without_interpolation(context, want_skip_space = true) location = @token.location unless string_literal_start? @@ -2344,7 +2348,7 @@ module Crystal string = parse_delimiter(want_skip_space) if string.is_a?(StringLiteral) - string.value + string else raise "interpolation not allowed in #{context}", location end @@ -2701,11 +2705,11 @@ module Crystal raise "can't require inside type declarations", @token if @type_nest > 0 next_token_skip_space - string = parse_string_without_interpolation("require") + string_literal = parse_string_literal_without_interpolation("require") skip_space - Require.new string + Require.new(string_literal.value).at_end(string_literal) end def parse_case From 536df1f6c7e1cad2762aad1fe735843f5828c0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 05:08:51 -0800 Subject: [PATCH 35/50] Add location of `begin ... end` --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 8d8be727510e..8017c66a6d67 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2210,6 +2210,7 @@ module Crystal assert_end_location "x : Foo ->" assert_end_location "x : Foo -> Bar" assert_end_location %(require "foo") + assert_end_location "begin; 1; 2; 3; end" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index c2cab366f1ea..a2892d9e85e9 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1426,13 +1426,14 @@ module Crystal next_token_skip_space if rescues || a_ensure - ex = ExceptionHandler.new(exp, rescues, a_else, a_ensure).at(begin_location).at_end(end_location) + ex = ExceptionHandler.new(exp, rescues, a_else, a_ensure) + ex.at(begin_location).at_end(end_location) ex.implicit = true if implicit ex.else_location = else_location ex.ensure_location = ensure_location {ex, end_location} else - exp + exp.at(begin_location) {exp, end_location} end end From 377aa74e8daf04935549d4ef511744f1f007e359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 05:41:30 -0800 Subject: [PATCH 36/50] Add end location of endless range literal --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 8017c66a6d67..2976d8bac4d2 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2211,6 +2211,7 @@ module Crystal assert_end_location "x : Foo -> Bar" assert_end_location %(require "foo") assert_end_location "begin; 1; 2; 3; end" + assert_end_location "1.." assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index a2892d9e85e9..afb383c21c6f 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -525,20 +525,22 @@ module Crystal end def new_range(exp, location, exclusive) + end_location = token_end_location check_void_value exp, location next_token_skip_space check_void_expression_keyword - right = if end_token? || - @token.type.op_rparen? || - @token.type.op_comma? || - @token.type.op_semicolon? || - @token.type.op_eq_gt? || - @token.type.newline? - Nop.new - else - parse_or - end - RangeLiteral.new(exp, right, exclusive).at(location).at_end(right) + if end_token? || + @token.type.op_rparen? || + @token.type.op_comma? || + @token.type.op_semicolon? || + @token.type.op_eq_gt? || + @token.type.newline? + right = Nop.new + else + right = parse_or + end_location = right.end_location + end + RangeLiteral.new(exp, right, exclusive).at(location).at_end(end_location) end macro parse_operator(name, next_operator, node, *operators, right_associative = false) @@ -969,6 +971,7 @@ module Crystal location = @token.location atomic = parse_atomic_without_location atomic.location ||= location + atomic.end_location ||= token_end_location atomic end From a75e4247b3caac94126eb0553005fcff89f71d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 05:43:53 -0800 Subject: [PATCH 37/50] Add end location of `.responds_to?` --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 2976d8bac4d2..a03a76b8f328 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2212,6 +2212,8 @@ module Crystal assert_end_location %(require "foo") assert_end_location "begin; 1; 2; 3; end" assert_end_location "1.." + assert_end_location "foo.responds_to?(:foo)" + assert_end_location "foo.responds_to? :foo" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index afb383c21c6f..0d09ad999855 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -921,16 +921,18 @@ module Crystal name = parse_responds_to_name next_token_skip_space_or_newline check :OP_RPAREN + end_location = token_end_location next_token_skip_space elsif @token.type.space? next_token name = parse_responds_to_name + end_location = token_end_location next_token_skip_space else unexpected_token "expected space or '('" end - RespondsTo.new(atomic, name) + RespondsTo.new(atomic, name).at_end(end_location) end def parse_responds_to_name From 839c307dda7bb90c7bf4b16bd1aaba035a98b8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 05:49:33 -0800 Subject: [PATCH 38/50] Add end location of `.nil?` --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index a03a76b8f328..863408bfb9f6 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2214,6 +2214,8 @@ module Crystal assert_end_location "1.." assert_end_location "foo.responds_to?(:foo)" assert_end_location "foo.responds_to? :foo" + assert_end_location "foo.nil?" + assert_end_location "foo.nil?( )" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 0d09ad999855..9efd193977db 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -944,15 +944,17 @@ module Crystal end def parse_nil?(atomic) + end_location = token_end_location next_token if @token.type.op_lparen? next_token_skip_space_or_newline check :OP_RPAREN + end_location = token_end_location next_token_skip_space end - IsA.new(atomic, Path.global("Nil"), nil_check: true) + IsA.new(atomic, Path.global("Nil"), nil_check: true).at_end(end_location) end def parse_negation_suffix(atomic) From 8f76dd473cbf591a9b897a3c5944ff97a69d285c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 05:58:11 -0800 Subject: [PATCH 39/50] Add end location of uninitialized var --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 863408bfb9f6..a45e9f92825b 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2216,6 +2216,8 @@ module Crystal assert_end_location "foo.responds_to? :foo" assert_end_location "foo.nil?" assert_end_location "foo.nil?( )" + assert_end_location "@a = uninitialized Foo" + assert_end_location "@@a = uninitialized Foo" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 9efd193977db..768ca441ceb5 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -423,7 +423,7 @@ module Crystal push_var atomic next_token_skip_space type = parse_bare_proc_type - atomic = UninitializedVar.new(atomic, type).at(location) + atomic = UninitializedVar.new(atomic, type).at(location).at_end(type) return atomic else if atomic.is_a?(Var) && !var?(atomic.name) From 981215f6ea990065bb2ebad1201d1522ed89693c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 06:00:49 -0800 Subject: [PATCH 40/50] Add end location of trailing `rescue`/`ensure` --- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/parser.cr | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index a45e9f92825b..50a086f465f1 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2218,6 +2218,8 @@ module Crystal assert_end_location "foo.nil?( )" assert_end_location "@a = uninitialized Foo" assert_end_location "@@a = uninitialized Foo" + assert_end_location "1 rescue 2" + assert_end_location "1 ensure 2" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 768ca441ceb5..94c3b635cc20 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -313,18 +313,22 @@ module Crystal rescue_body = parse_op_assign rescues = [Rescue.new(rescue_body)] of Rescue if atomic.is_a?(Assign) - atomic.value = ExceptionHandler.new(atomic.value, rescues).at(location).tap { |e| e.suffix = true } + atomic.value = ex = ExceptionHandler.new(atomic.value, rescues) else - atomic = ExceptionHandler.new(atomic, rescues).at(location).tap { |e| e.suffix = true } + atomic = ex = ExceptionHandler.new(atomic, rescues) end + ex.at(location).at_end(rescue_body) + ex.suffix = true when Keyword::ENSURE next_token_skip_space ensure_body = parse_op_assign if atomic.is_a?(Assign) - atomic.value = ExceptionHandler.new(atomic.value, ensure: ensure_body).at(location).tap { |e| e.suffix = true } + atomic.value = ex = ExceptionHandler.new(atomic.value, ensure: ensure_body) else - atomic = ExceptionHandler.new(atomic, ensure: ensure_body).at(location).tap { |e| e.suffix = true } + atomic = ex = ExceptionHandler.new(atomic, ensure: ensure_body) end + ex.at(location).at_end(ensure_body) + ex.suffix = true else break end From d1407ac1706392459770cccbffa1ac9c43551b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 06:06:47 -0800 Subject: [PATCH 41/50] Add end location of property assignment with splat --- spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 50a086f465f1..a99b73a333f6 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2220,6 +2220,7 @@ module Crystal assert_end_location "@@a = uninitialized Foo" assert_end_location "1 rescue 2" assert_end_location "1 ensure 2" + assert_end_location "foo.bar= *baz" assert_syntax_error %({"a" : 1}), "space not allowed between named argument name and ':'" assert_syntax_error %({"a": 1, "b" : 2}), "space not allowed between named argument name and ':'" diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 94c3b635cc20..9331eff657d2 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -869,9 +869,10 @@ module Crystal def parse_single_arg if @token.type.op_star? + location = @token.location next_token_skip_space arg = parse_op_assign_no_control - Splat.new(arg) + Splat.new(arg).at(location).at_end(arg) else parse_op_assign_no_control end From 89c19586593bbae6777d00835171154565f0ee23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 08:28:51 -0800 Subject: [PATCH 42/50] Don't set end_location in parse_atomic --- src/compiler/crystal/syntax/parser.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 9331eff657d2..38db55a7cd89 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -980,7 +980,6 @@ module Crystal location = @token.location atomic = parse_atomic_without_location atomic.location ||= location - atomic.end_location ||= token_end_location atomic end From 0df6a12e7787ddc1069282c249adb27810bcd286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 08:55:26 -0800 Subject: [PATCH 43/50] Don't change location of exception handler body --- src/compiler/crystal/syntax/parser.cr | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 38db55a7cd89..be6675de3211 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1374,10 +1374,10 @@ module Crystal next_token_skip_statement_end exps = parse_expressions node, end_location = parse_exception_handler exps, begin_location: begin_location - node.end_location = end_location if !node.is_a?(ExceptionHandler) && (!node.is_a?(Expressions) || !node.keyword.none?) - node = Expressions.new([node]).at(begin_location).at_end(end_location) + node = Expressions.new([node]) end + node.at(begin_location).at_end(end_location) node.keyword = :begin if node.is_a?(Expressions) node end @@ -1444,7 +1444,6 @@ module Crystal ex.ensure_location = ensure_location {ex, end_location} else - exp.at(begin_location) {exp, end_location} end end From a958e3c5f6c5521c59451d72c97b125184e3a8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 22 Nov 2022 10:02:54 -0800 Subject: [PATCH 44/50] Add end location of local var --- spec/compiler/parser/parser_spec.cr | 6 ++++++ src/compiler/crystal/syntax/ast.cr | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index a99b73a333f6..899f2a69ae37 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2485,6 +2485,12 @@ module Crystal source_between(source, node.location, node.end_location).should eq("1, 2") end + it "sets correct location of var in type declaration", focus: true do + source = "foo : Int32" + node = Parser.new(source).parse.as(TypeDeclaration).var + source_between(source, node.location, node.end_location).should eq("foo") + end + it "doesn't override yield with macro yield" do parser = Parser.new("def foo; yield 1; {% begin %} yield 1 {% end %}; end") a_def = parser.parse.as(Def) diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index e21edbf9936c..420cd64f30cc 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -575,6 +575,13 @@ module Crystal Var.new(@name) end + def end_location + return @end_location if @end_location + return unless loc = @location + + Location.new(loc.filename, loc.line_number, loc.column_number + name_size - 1) + end + def_equals name def_hash name end From af8d754cc7ac7252715bf270b4c8962ff4fa42cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:49:41 -0800 Subject: [PATCH 45/50] Delete `focus: true` --- spec/compiler/parser/parser_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 899f2a69ae37..34533ec79378 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2485,7 +2485,7 @@ module Crystal source_between(source, node.location, node.end_location).should eq("1, 2") end - it "sets correct location of var in type declaration", focus: true do + it "sets correct location of var in type declaration" do source = "foo : Int32" node = Parser.new(source).parse.as(TypeDeclaration).var source_between(source, node.location, node.end_location).should eq("foo") From e3e29e0b31e5088438513c0e7eddee74b55a7ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:05:27 -0800 Subject: [PATCH 46/50] Add location of array literal element --- spec/compiler/parser/parser_spec.cr | 18 ++++++++++++++---- src/compiler/crystal/syntax/lexer.cr | 4 ++-- src/compiler/crystal/syntax/parser.cr | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 34533ec79378..534f01f46022 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -36,16 +36,19 @@ private def source_between(string, loc, end_loc) string[beginning..ending] end +private def node_source(string, node) + source_between(string, node.location, node.end_location) +end + private def assert_end_location(source, line_number = 1, column_number = source.size, file = __FILE__, line = __LINE__) it "gets corrects end location for #{source.inspect}", file, line do string = "#{source}; 1" parser = Parser.new(string) node = parser.parse.as(Expressions).expressions[0] - loc = node.location.not_nil! + node_source(string, node).should eq(source) end_loc = node.end_location.not_nil! end_loc.line_number.should eq(line_number) end_loc.column_number.should eq(column_number) - source_between(string, loc, end_loc).should eq(source) end end @@ -2479,16 +2482,23 @@ module Crystal source_between(source, node.name_location, node.name_end_location).should eq("foo") end + it "sets correct location of element in array literal" do + source = "%i(foo bar)" + elements = Parser.new(source).parse.as(ArrayLiteral).elements + node_source(source, elements[0]).should eq("foo") + node_source(source, elements[1]).should eq("bar") + end + it "sets correct location of implicit tuple literal of multi-return" do source = "def foo; return 1, 2; end" node = Parser.new(source).parse.as(Def).body.as(Return).exp.not_nil! - source_between(source, node.location, node.end_location).should eq("1, 2") + node_source(source, node).should eq("1, 2") end it "sets correct location of var in type declaration" do source = "foo : Int32" node = Parser.new(source).parse.as(TypeDeclaration).var - source_between(source, node.location, node.end_location).should eq("foo") + node_source(source, node).should eq("foo") end it "doesn't override yield with macro yield" do diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index c8f65187a448..5437d7cec66b 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -2307,8 +2307,6 @@ module Crystal end def next_string_array_token - reset_token - while true if current_char == '\n' next_char @@ -2320,6 +2318,8 @@ module Crystal end end + reset_token + if current_char == @token.delimiter_state.end @token.raw = current_char.to_s if @wants_raw next_char diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index be6675de3211..f73dbf98994f 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2381,7 +2381,7 @@ module Crystal next_string_array_token case @token.type when .string? - strings << klass.new(@token.value.to_s) + strings << klass.new(@token.value.to_s).at(@token.location).at_end(token_end_location) when .string_array_end? end_location = token_end_location next_token From 0032caf1d7feaadae2dd96486f49a0f7ae408706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:16:11 -0800 Subject: [PATCH 47/50] Add location of trailing ensure --- spec/compiler/parser/parser_spec.cr | 8 ++++++++ src/compiler/crystal/syntax/parser.cr | 2 ++ 2 files changed, 10 insertions(+) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 534f01f46022..1060f5723f4d 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2476,6 +2476,14 @@ module Crystal node.end_location.not_nil!.line_number.should eq(5) end + it "sets correct location of trailing ensure" do + parser = Parser.new("foo ensure bar") + node = parser.parse.as(ExceptionHandler) + ensure_location = node.ensure_location.not_nil! + ensure_location.line_number.should eq(1) + ensure_location.column_number.should eq(5) + end + it "sets correct location of call name" do source = "foo(bar)" node = Parser.new(source).parse.as(Call) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index f73dbf98994f..422966c3050c 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -320,6 +320,7 @@ module Crystal ex.at(location).at_end(rescue_body) ex.suffix = true when Keyword::ENSURE + ensure_location = @token.location next_token_skip_space ensure_body = parse_op_assign if atomic.is_a?(Assign) @@ -328,6 +329,7 @@ module Crystal atomic = ex = ExceptionHandler.new(atomic, ensure: ensure_body) end ex.at(location).at_end(ensure_body) + ex.ensure_location = ensure_location ex.suffix = true else break From b511000641db01693c5706bdafe7bcd318658955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 08:20:32 -0800 Subject: [PATCH 48/50] Add location of trailing rescue --- spec/compiler/parser/parser_spec.cr | 7 +++++++ src/compiler/crystal/syntax/parser.cr | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 1060f5723f4d..e642a703ad5d 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2484,6 +2484,13 @@ module Crystal ensure_location.column_number.should eq(5) end + it "sets correct location of trailing rescue" do + source = "foo rescue bar" + parser = Parser.new(source) + node = parser.parse.as(ExceptionHandler).rescues.not_nil![0] + node_source(source, node).should eq("rescue bar") + end + it "sets correct location of call name" do source = "foo(bar)" node = Parser.new(source).parse.as(Call) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 422966c3050c..7332d59cea0f 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -309,9 +309,10 @@ module Crystal when Keyword::UNTIL raise "trailing `until` is not supported", @token when Keyword::RESCUE + rescue_location = @token.location next_token_skip_space rescue_body = parse_op_assign - rescues = [Rescue.new(rescue_body)] of Rescue + rescues = [Rescue.new(rescue_body).at(rescue_location).at_end(rescue_body)] of Rescue if atomic.is_a?(Assign) atomic.value = ex = ExceptionHandler.new(atomic.value, rescues) else From 0da7e2e19bbf49cfd7d158c5a2b2e7259fbe97d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:36:25 -0800 Subject: [PATCH 49/50] Delete pseudo `Var#end_location` --- spec/compiler/parser/parser_spec.cr | 31 +++++++++++++++++++++++++++ src/compiler/crystal/syntax/ast.cr | 7 ------ src/compiler/crystal/syntax/parser.cr | 18 +++++++++------- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index e642a703ad5d..5e8c510e6b23 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -2514,6 +2514,37 @@ module Crystal source = "foo : Int32" node = Parser.new(source).parse.as(TypeDeclaration).var node_source(source, node).should eq("foo") + + source = "begin : Int32" + node = Parser.new(source).parse.as(TypeDeclaration).var + node_source(source, node).should eq("begin") + end + + it "sets correct location of var in proc pointer" do + source = "foo : Foo; ->foo.bar" + expressions = Parser.new(source).parse.as(Expressions).expressions + node = expressions[1].as(ProcPointer).obj.not_nil! + node_source(source, node).should eq("foo") + end + + it "sets correct location of var in macro for loop" do + source = "{% for foo, bar in baz %} {% end %}" + node = Parser.new(source).parse.as(MacroFor) + node_source(source, node.vars[0]).should eq("foo") + node_source(source, node.vars[1]).should eq("bar") + end + + it "sets correct location of receiver var in method def" do + source = "def foo.bar; end" + node = Parser.new(source).parse.as(Def).receiver.not_nil! + node_source(source, node).should eq("foo") + end + + it "sets correct location of vars in C struct" do + source = "lib Foo; struct Bar; fizz, buzz : Int32; end; end" + expressions = Parser.new(source).parse.as(LibDef).body.as(CStructOrUnionDef).body.as(Expressions).expressions + node_source(source, expressions[0].as(TypeDeclaration).var).should eq("fizz") + node_source(source, expressions[1].as(TypeDeclaration).var).should eq("buzz") end it "doesn't override yield with macro yield" do diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index 420cd64f30cc..e21edbf9936c 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -575,13 +575,6 @@ module Crystal Var.new(@name) end - def end_location - return @end_location if @end_location - return unless loc = @location - - Location.new(loc.filename, loc.line_number, loc.column_number + name_size - 1) - end - def_equals name def_hash name end diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 7332d59cea0f..593dd3d53160 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1227,7 +1227,7 @@ module Crystal def check_type_declaration if next_comes_colon_space? name = @token.value.to_s - var = Var.new(name).at(@token.location) + var = Var.new(name).at(@token.location).at_end(token_end_location) next_token_skip_space check :OP_COLON type_declaration = parse_type_declaration(var) @@ -1984,6 +1984,8 @@ module Crystal case @token.type when .ident? name = @token.value.to_s + var_location = @token.location + var_end_location = token_end_location global_call = global equals_sign, end_location = consume_def_equals_sign_skip_space if equals_sign @@ -1993,7 +1995,7 @@ module Crystal if name != "self" && !var_in_scope?(name) raise "undefined variable '#{name}'", location end - obj = Var.new(name) + obj = Var.new(name).at(var_location).at_end(var_end_location) name = consume_def_or_macro_name equals_sign, end_location = consume_def_equals_sign_skip_space @@ -3357,7 +3359,7 @@ module Crystal else unexpected_token "expecting ident or underscore" end - vars << Var.new(var).at(@token.location) + vars << Var.new(var).at(@token.location).at_end(token_end_location) next_token_skip_space if @token.type.op_comma? @@ -3561,7 +3563,7 @@ module Crystal if @token.type.op_period? unless receiver if name - receiver = Var.new(name).at(receiver_location) + receiver = Var.new(name).at(receiver_location).at_end(end_location) else raise "shouldn't reach this line" end @@ -4317,7 +4319,7 @@ module Crystal end else if @no_type_declaration == 0 && @token.type.op_colon? - declare_var = parse_type_declaration(Var.new(name).at(location)) + declare_var = parse_type_declaration(Var.new(name).at(location).at_end(end_location)) end_location = declare_var.end_location # Don't declare a local variable if it happens directly as an argument @@ -5934,13 +5936,13 @@ module Crystal end def parse_c_struct_or_union_fields(exps) - vars = [Var.new(@token.value.to_s).at(@token.location)] + vars = [Var.new(@token.value.to_s).at(@token.location).at_end(token_end_location)] next_token_skip_space_or_newline while @token.type.op_comma? next_token_skip_space_or_newline - vars << Var.new(check_ident).at(@token.location) + vars << Var.new(check_ident).at(@token.location).at_end(token_end_location) next_token_skip_space_or_newline end @@ -5952,7 +5954,7 @@ module Crystal skip_statement_end vars.each do |var| - exps << TypeDeclaration.new(var, type).at(var).at_end(type) + exps << TypeDeclaration.new(var, type) end end From ccb84e329f48f35e6ea8dfde5efb7bd9b74512c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:59:19 -0800 Subject: [PATCH 50/50] Add back TypeDeclaration location --- src/compiler/crystal/syntax/parser.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 593dd3d53160..ce4e052fe9eb 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5954,7 +5954,7 @@ module Crystal skip_statement_end vars.each do |var| - exps << TypeDeclaration.new(var, type) + exps << TypeDeclaration.new(var, type).at(var).at_end(type) end end