From 88e73959dd330f3fde6b2d0caeecfd974f6faa4b Mon Sep 17 00:00:00 2001 From: Vince Broz Date: Sat, 1 Feb 2025 15:13:33 -0500 Subject: [PATCH] Fix array-within-word-array parsing When yard saw a constant like: ```ruby FOO = [%w[bar baz]] ``` it cut off the array at the first closing bracket: ```ruby [%w[bar baz] ``` Fix what appears to be a copy-and-paste error in the parser by deleting two seemingly incorrect lines. --- lib/yard/parser/ruby/ruby_parser.rb | 6 +-- spec/parser/ruby/ruby_parser_spec.rb | 55 +++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/yard/parser/ruby/ruby_parser.rb b/lib/yard/parser/ruby/ruby_parser.rb index a7ba5fabf..8bd8dc1fc 100644 --- a/lib/yard/parser/ruby/ruby_parser.rb +++ b/lib/yard/parser/ruby/ruby_parser.rb @@ -390,16 +390,12 @@ def on_aref_field(*args) def on_array(other) node = AstNode.node_class_for(:array).new(:array, [other]) - map = @map[MAPPINGS[node.type]] + map = @map[MAPPINGS[node.type]] if other.nil? || other.type == :list if map && !map.empty? lstart, sstart = *map.pop node.source_range = Range.new(sstart, @ns_charno - 1) node.line_range = Range.new(lstart, lineno) else - sstart = other.source_range.begin - lstart = other.line_range.begin - node.source_range = Range.new(sstart, @ns_charno - 1) - node.line_range = Range.new(lstart, lineno) node.source_range = other.source_range node.line_range = other.line_range end diff --git a/spec/parser/ruby/ruby_parser_spec.rb b/spec/parser/ruby/ruby_parser_spec.rb index 62cba766a..c66dc1527 100644 --- a/spec/parser/ruby/ruby_parser_spec.rb +++ b/spec/parser/ruby/ruby_parser_spec.rb @@ -283,6 +283,11 @@ def hello; end src = "%#{tok}{\na b c\n d e f\n}" expect(stmt(src).source).to eq src end + + it "shows proper source for %#{tok}[] array" do + src = "%#{tok}[\na b c\n d e f\n]" + expect(stmt(src).source).to eq src + end end {'i' => :qsymbols_literal, 'I' => :symbols_literal, @@ -300,6 +305,33 @@ def hello; end end end + it "parses %#{id}{...} literals" do + [ + "TEST = %#{id}{A B C}", + "TEST = %#{id}{ A B C }", + "TEST = %#{id}{ \nA \nB \nC \n}", + "TEST = %#{id}{\n\nAD\n\nB\n\nC\n\n}", + "TEST = %#{id}{\n A\n B\n C\n }", + ].each do |str| + node = stmt(str).jump(sym) + expect(node.source).to eq(str[/(\%#{id}\{.+\})/m, 1]) + end + end + + it "parses %#{id}[...] literals" do + [ + "TEST = %#{id}[A B C]", + "TEST = %#{id}[ A B C ]", + "TEST = %#{id}[ \nA \nB \nC \n]", + "TEST = %#{id}[\n\nAD\n\nB\n\nC\n\n]", + "TEST = %#{id}[\n A\n B\n C\n ]", + "TEST = %#{id}[\n A]", + ].each do |str| + node = stmt(str).jump(sym) + expect(node.source).to eq(str[/(\%#{id}\[.+\])/m, 1]) + end + end + it "tokenizing %#{id}(...) returns correct tokens" do toks = tokenize("TEST = %#{id}(A B C)").flatten expect(toks.count(:tstring_content)).to eq(3) @@ -327,7 +359,7 @@ def method # Method comment not docstring end eof - + tokens = tokenize(src.gsub(/^ +/, '')) expect(tokens).to eq(tokens.sort_by {|t| t.last }) expect(tokens.map {|t| t.first }).to eq %i(kw sp ident nl comment kw nl) @@ -370,6 +402,27 @@ class Foo expect(s.jump(:array).source).to eq "['foo', 'bar']" end + it "parses [[]] as array of arrays" do + s = stmt(<<-eof) + class Foo + FOO = [['foo', 'bar']] + end + eof + expect(s.jump(:array).source).to eq "[['foo', 'bar']]" + end + + it "parses %w() array inside another empty array" do + s = stmts(<<-eof) + FOO = [%w( foo bar )] + eof + expect(s[0].jump(:array).source).to eq '[%w( foo bar )]' + end + + it "parses %w() array inside another populated array" do + s = stmts("FOO = ['1', %w[]]") + expect(s[0].jump(:array).source).to eq "['1', %w[]]" + end + it "shows source for unary minus" do expect(stmt("X = - 1").jump(:unary).source).to eq '- 1' end