Skip to content

Commit

Permalink
Fix array-within-word-array parsing
Browse files Browse the repository at this point in the history
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 by ensuring that these types of arrays, which don't have a
starting :lbracket, don't pop off :lbracket state in @Map.

Drop dead code and add a variety of related regression cases found in
my application using sord after trying different fixes.
  • Loading branch information
apiology committed Feb 3, 2025
1 parent efb33b5 commit a5dcc55
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/yard/parser/ruby/ruby_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ 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)
Expand Down
55 changes: 54 additions & 1 deletion spec/parser/ruby/ruby_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit a5dcc55

Please sign in to comment.