[前][次][番号順一覧][スレッド一覧]

ruby-changes:64752

From: aycabta <ko1@a...>
Date: Tue, 5 Jan 2021 18:13:50 +0900 (JST)
Subject: [ruby-changes:64752] 0123bc9d38 (master): [ruby/irb] Use error tokens if there are no correct tokens in the same place

https://git.ruby-lang.org/ruby.git/commit/?id=0123bc9d38

From 0123bc9d3851c79338b9df509a82b74c93371381 Mon Sep 17 00:00:00 2001
From: aycabta <aycabta@g...>
Date: Mon, 4 Jan 2021 21:11:24 +0900
Subject: [ruby/irb] Use error tokens if there are no correct tokens in the
 same place

For example, the broken code "%www" will result in only one error token.

https://github.com/ruby/irb/commit/9fa39a7cf3

diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 72135c8..e7adfb4 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -66,7 +66,6 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L66
         unprocessed_tokens = []
         line_num_offset = 0
         tokens.each do |t|
-          next if t[1] == :on_parse_error || t[1] == :compile_error
           partial_tokens << t
           unprocessed_tokens << t
           if t[2].include?("\n")
@@ -107,13 +106,35 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L106
     end
   end
 
+  ERROR_TOKENS = [
+    :on_parse_error,
+    :compile_error,
+    :on_assign_error,
+    :on_alias_error,
+    :on_class_name_error,
+    :on_param_error
+  ]
+
   def ripper_lex_without_warning(code)
     verbose, $VERBOSE = $VERBOSE, nil
     tokens = nil
     self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
       lexer = Ripper::Lexer.new(inner_code, '-', line_no)
       if lexer.respond_to?(:scan) # Ruby 2.7+
-        tokens = lexer.scan
+        tokens = []
+        pos_to_index = {}
+        lexer.scan.each do |t|
+          if pos_to_index.has_key?(t[0])
+            index = pos_to_index[t[0]]
+            found_tk = tokens[index]
+            if ERROR_TOKENS.include?(found_tk[1]) && !ERROR_TOKENS.include?(t[1])
+              tokens[index] = t
+            end
+          else
+            pos_to_index[t[0]] = tokens.size
+            tokens << t
+          end
+        end
       else
         tokens = lexer.parse
       end
@@ -128,7 +149,6 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L149
     prev_spaces = md.nil? ? 0 : md[1].count(' ')
     line_count = 0
     @tokens.each_with_index do |t, i|
-      next if t[1] == :on_parse_error || t[1] == :compile_error
       if t[2].include?("\n")
         line_count += t[2].count("\n")
         if line_count >= line_index
@@ -357,7 +377,6 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L377
     indent = 0
     in_oneliner_def = nil
     tokens.each_with_index { |t, index|
-      next if t[1] == :on_parse_error || t[1] == :compile_error
       # detecting one-liner method definition
       if in_oneliner_def.nil?
         if t[3].allbits?(Ripper::EXPR_ENDFN)
@@ -443,7 +462,6 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L462
     open_brace_on_line = 0
     in_oneliner_def = nil
     @tokens.each_with_index do |t, index|
-      next if t[1] == :on_parse_error || t[1] == :compile_error
       # detecting one-liner method definition
       if in_oneliner_def.nil?
         if t[3].allbits?(Ripper::EXPR_ENDFN)
@@ -513,7 +531,6 @@ class RubyLex https://github.com/ruby/ruby/blob/trunk/lib/irb/ruby-lex.rb#L531
     open_brace_on_line = 0
     in_oneliner_def = nil
     @tokens.each_with_index do |t, index|
-      next if t[1] == :on_parse_error || t[1] == :compile_error
       # detecting one-liner method definition
       if in_oneliner_def.nil?
         if t[3].allbits?(Ripper::EXPR_ENDFN)
diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb
index 0c7c9f2..ed4944a 100644
--- a/test/irb/test_ruby_lex.rb
+++ b/test/irb/test_ruby_lex.rb
@@ -442,5 +442,37 @@ module TestIRB https://github.com/ruby/ruby/blob/trunk/test/irb/test_ruby_lex.rb#L442
       expected_prompt_list = input_with_prompt.map(&:prompt)
       assert_dynamic_prompt(lines, expected_prompt_list)
     end
+
+    def test_broken_percent_literal
+      if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
+        skip 'This test needs Ripper::Lexer#scan to take broken tokens'
+      end
+
+      ruby_lex = RubyLex.new
+      tokens = ruby_lex.ripper_lex_without_warning('%wwww')
+      pos_to_index = {}
+      tokens.each_with_index { |t, i|
+        assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
+        pos_to_index[t[0]] = i
+      }
+    end
+
+    def test_broken_percent_literal_in_method
+      if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
+        skip 'This test needs Ripper::Lexer#scan to take broken tokens'
+      end
+
+      ruby_lex = RubyLex.new
+      tokens = ruby_lex.ripper_lex_without_warning(<<~EOC.chomp)
+        def foo
+          %wwww
+        end
+      EOC
+      pos_to_index = {}
+      tokens.each_with_index { |t, i|
+        assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
+        pos_to_index[t[0]] = i
+      }
+    end
   end
 end
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]