yard icon indicating copy to clipboard operation
yard copied to clipboard

Right hand assignment inside case/when/end causes cannot get the first element of beginless range (RangeError)

Open DimaD opened this issue 3 years ago • 3 comments

Yards AST parser raises cannot get the first element of beginless range (RangeError) when using right-hand assignment and restructuring.

I have tried using restructuring in different contexts and only the use from the examples below inside of the case statement causes an error.

Steps to reproduce

Example 1 - causes an error

case 1
when 1
  [1] => [a]
end

Example 2 - causes an error

case 1
when 1
  {a: 1} => {a:}
end

Example 3 - no error The error does not occur in every context, I was only able to trigger it inside the case/when statement.

def m
  [1] => [a]
end

Actual Output

% be yardoc --debug broken-yard.rb                                            
[debug]: Parsing ["broken-yard.rb"] with `ruby` parser
[debug]: Parsing broken-yard.rb
bundler: failed to load command: yardoc (/Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/bin/yardoc)
/Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:274:in `first': cannot get the first element of beginless range (RangeError)
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:274:in `line'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:624:in `block in insert_comments'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:212:in `traverse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:615:in `insert_comments'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:60:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:17:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:442:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:46:in `block in parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/logging.rb:82:in `capture'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:45:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:371:in `parse_in_order'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:114:in `block in parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/logging.rb:182:in `enter_level'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:113:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard.rb:20:in `parse'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:259:in `block in run'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/serializers/yardoc_serializer.rb:56:in `lock_for_writing'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/registry_store.rb:202:in `lock_for_writing'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/registry.rb:210:in `lock_for_writing'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:258:in `run'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/lib/yard/cli/command.rb:14:in `run'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/gems/yard-0.9.27/bin/yardoc:13:in `<top (required)>'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/bin/yardoc:23:in `load'
	from /Users/testuser/src/yard_examples/vendor/bundle/ruby/3.0.0/bin/yardoc:23:in `<top (required)>'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli/exec.rb:58:in `load'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli/exec.rb:58:in `kernel_load'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli/exec.rb:23:in `run'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli.rb:478:in `exec'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli.rb:31:in `dispatch'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/cli.rb:25:in `start'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/libexec/bundle:49:in `block in <top (required)>'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/3.0.0/bundler/friendly_errors.rb:103:in `with_friendly_errors'
	from /Users/testuser/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/libexec/bundle:37:in `<top (required)>'
	from /Users/testuser/.rbenv/versions/3.0.3/bin/bundle:23:in `load'
	from /Users/testuser/.rbenv/versions/3.0.3/bin/bundle:23:in `<main>'

Expected Output

I expect no errors and no docs produced from the example

Files:           0
Modules:         0 (    0 undocumented)
Classes:         0 (    0 undocumented)
Constants:       0 (    0 undocumented)
Attributes:      0 (    0 undocumented)
Methods:         0 (    0 undocumented)
 100.00% documented

Environment details:

  • OS: macOS 11.6.2 (20G314)
  • Ruby version (ruby -v): ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [arm64-darwin20]
  • YARD version (yard -v): yard 0.9.27

I have read the Contributing Guide.

DimaD avatar Mar 15 '22 06:03 DimaD

I have a similar failure, but with a different pattern (heavily stripped down)

    case [self, other]
    in [MetaType[id], *]
      if other in MetaType[id2]
      end
    end
/opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:274:in `first': cannot get the first element of beginless range (RangeError)
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:274:in `line'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:624:in `block in insert_comments'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:212:in `traverse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:615:in `insert_comments'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:60:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:17:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:442:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:46:in `block in parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/logging.rb:82:in `capture'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:45:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:371:in `parse_in_order'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:114:in `block in parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/logging.rb:182:in `enter_level'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/source_parser.rb:113:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard.rb:20:in `parse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:259:in `block in run'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/serializers/yardoc_serializer.rb:56:in `lock_for_writing'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/registry_store.rb:202:in `lock_for_writing'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/registry.rb:210:in `lock_for_writing'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/cli/yardoc.rb:258:in `run'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/cli/command.rb:14:in `run'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/bin/yardoc:13:in `<top (required)>'
	from /opt/local/bin/yardoc:25:in `load'
	from /opt/local/bin/yardoc:25:in `<main>'

akimd avatar Apr 04 '22 17:04 akimd

I believe the proper fix is in lib/yard/parser/ruby/ast_node.rb to replace:

        def line
          line_range && line_range.first
        end

by

        def line
          line_range && line_range.begin
        end

since begin always returns the "first" element, as nil if it does not exist (while first dies if begin is nil).

However in that case yardoc dies farther:

/opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:624:in `block in insert_comments': undefined method `-' for nil:NilClass (NoMethodError)

            ((node.line - 1).downto(node.line - 2).to_a + [node.line]).each do |line|
                        ^
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ast_node.rb:212:in `traverse'
	from /opt/local/lib/ruby3.1/gems/3.1.0/gems/yard-0.9.27/lib/yard/parser/ruby/ruby_parser.rb:615:in `insert_comments'

so it looks like after all it does not like line to return nil, contrary to what the && seems to imply. So I made it

        def line
          line_range && line_range.begin || 1
        end

and this time yardoc finishes properly.

akimd avatar Apr 04 '22 17:04 akimd

It turns out I also need to change reset_line_info in the same file:

            self.line_range = Range.new(f.line_range.begin || 1, l.line_range.last)
            self.source_range = Range.new(f.source_range.begin || 1, l.source_range.last)

instead of using .first.

akimd avatar Apr 04 '22 17:04 akimd