diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb index 3abb53b4e..c50e29959 100644 --- a/lib/irb/ruby-lex.rb +++ b/lib/irb/ruby-lex.rb @@ -158,7 +158,7 @@ def code_terminated?(code, tokens, opens, local_variables:) when :other_error opens.empty? && !should_continue?(tokens) when :valid - !should_continue?(tokens) + !should_continue?(tokens, syntax_valid: true) end end @@ -180,7 +180,7 @@ def assignment_expression?(code, local_variables:) $VERBOSE = verbose end - def should_continue?(tokens) + def should_continue?(tokens, syntax_valid: nil) # Look at the last token and check if IRB need to continue reading next line. # Example code that should continue: `a\` `a +` `a.` # Trailing spaces, newline, comments are skipped @@ -197,6 +197,11 @@ def should_continue?(tokens) # Endless range should not continue return false if token.event == :on_op && token.tok.match?(/\A\.\.\.?\z/) + # EXPR_BEG with on_op (* or **) + # If syntax is valid (`x in *`, `x in **`, `def f(*,**)=f *, **`) it should not continue + # If syntax validness is unknown (maybe part of case-in, multiply, power) it should continue + return false if syntax_valid && token.event == :on_op && token.tok.match?(/\A\*\*?\z/) + # EXPR_DOT and most of the EXPR_BEG should continue return token.state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_DOT) end diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index 9392b49fa..aa31ddde0 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -63,6 +63,8 @@ def test_should_continue assert_should_continue(['a+'], true) assert_should_continue(['a; #comment', '', '=begin', 'embdoc', '=end', ''], false) assert_should_continue(['a+ #comment', '', '=begin', 'embdoc', '=end', ''], true) + assert_should_continue(['x. in *'], true) + assert_should_continue(['x. in **'], true) end def test_code_block_open_with_should_continue @@ -80,6 +82,16 @@ def test_code_block_open_with_should_continue assert_code_block_open(['@; a'], false) assert_code_block_open(['@; a+'], true) assert_code_block_open(['@; (a'], true) + + # pattern match should not continue + if RUBY_VERSION >= '3.1.0' + assert_code_block_open(['x in *'], false) + assert_code_block_open(['x in **'], false) + end + + # multiply and power should continue + assert_code_block_open(['x. in *'], true) + assert_code_block_open(['x. in **'], true) end def test_broken_percent_literal