yard
                                
                                
                                
                                    yard copied to clipboard
                            
                            
                            
                        Right hand assignment inside case/when/end causes cannot get the first element of beginless range (RangeError)
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.
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>'
                                    
                                    
                                    
                                
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.
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.