Skip to content

Commit 4a23307

Browse files
committed
Deprecate Prism.lex_ripper
Since `on_sp` is emitted, it doesn't do a whole lot anymore. This leaves one incompatibility for code like ``"x#$%"`` Ripper confuses this for bare interpolation with a global, but `$%` is not a valid global name. Still, it emits two string tokens in such a case. It doesn't make sense for prism to work around this bug, so the affected files are added as excludes.
1 parent 6a67b0e commit 4a23307

File tree

6 files changed

+46
-37
lines changed

6 files changed

+46
-37
lines changed

bin/prism

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,10 @@ module Prism
210210

211211
# bin/prism lex_compat [source]
212212
def lex_compat(argv)
213+
require "ripper"
213214
source, filepath = read_source(argv)
214215

215-
ripper_value =
216-
begin
217-
Prism.lex_ripper(source)
218-
rescue ArgumentError, SyntaxError
219-
# If Ripper raises a syntax error, we want to continue as if it didn't
220-
# return any tokens at all. prism won't raise a syntax error, so it's
221-
# nicer to still be able to see the tokens that prism generated.
222-
[]
223-
end
224-
216+
ripper_value = Ripper.lex(source)
225217
prism_compat = Prism.lex_compat(source, filepath: filepath)
226218
prism = Prism.lex(source, filepath: filepath)
227219

lib/prism.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ def self.lex_compat(source, **options)
7171
# :call-seq:
7272
# Prism::lex_ripper(source) -> Array
7373
#
74-
# This wraps the result of Ripper.lex. It produces almost exactly the
75-
# same tokens. Raises SyntaxError if the syntax in source is invalid.
74+
# Deprecated. Directly use `Ripper.lex` instead.
7675
def self.lex_ripper(source)
7776
LexRipper.new(source).result # steep:ignore
7877
end

lib/prism/lex_ripper.rb

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# frozen_string_literal: true
22
# :markup: markdown
33

4+
warn(<<~MSG, uplevel: 4, category: :deprecated)
5+
[deprecation]: `Prism.lex_ripper` is deprecated and wil be removed in a future version. \
6+
Use `Ripper.lex` directly.
7+
MSG
8+
49
require "ripper"
510

611
module Prism
@@ -14,25 +19,7 @@ def initialize(source)
1419
end
1520

1621
def result
17-
previous = [] #: [[Integer, Integer], Symbol, String, untyped] | []
18-
results = [] #: Array[[[Integer, Integer], Symbol, String, untyped]]
19-
20-
lex(source).each do |token|
21-
case token[1]
22-
when :on_tstring_content
23-
if previous[1] == :on_tstring_content && (token[2].start_with?("\#$") || token[2].start_with?("\#@"))
24-
previous[2] << token[2]
25-
else
26-
results << token
27-
previous = token
28-
end
29-
else
30-
results << token
31-
previous = token
32-
end
33-
end
34-
35-
results
22+
lex(source)
3623
end
3724

3825
private

rakelib/lex.rake

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# frozen_string_literal: true
22
# typed: ignore
33

4+
require "ripper"
5+
46
module Prism
5-
# This class is responsible for lexing files with both lex_compat and
6-
# lex_ripper and ensuring they match up. It keeps track of the files which
7+
# This class is responsible for lexing files with both prism and
8+
# ripper and ensuring they match up. It keeps track of the files which
79
# failed to match up, and the files which passed.
810
class LexTask
911
attr_reader :failing_files, :passing_file_count
@@ -28,7 +30,7 @@ module Prism
2830
end
2931

3032
result = Prism.lex_compat(source)
31-
if result.errors.empty? && Prism.lex_ripper(source) == result.value
33+
if result.errors.empty? && Ripper.lex(source) == result.value
3234
@passing_file_count += 1
3335
true
3436
else
@@ -98,6 +100,10 @@ TARGETS = {
98100

99101
# Requires an implicit -x, which ripper does not respect
100102
"tool/merger.rb",
103+
104+
# Contains `"x#$%"` which looks like bare interpolation but $% is not a valid global.
105+
# This confuses ripper, it emits two string tokens.
106+
"ext/psych/lib/psych/scalar_scanner.rb",
101107
]
102108
},
103109
discourse: {
@@ -265,6 +271,16 @@ TOP_100_GEMS_INVALID_SYNTAX_PREFIXES = %w[
265271
top-100-gems/devise-4.9.2/lib/generators/templates/controllers/
266272
top-100-gems/fastlane-2.212.1/fastlane/lib/assets/custom_action_template.rb
267273
]
274+
TOP_100_GEMS_LEX_RIPPER_BUG = [
275+
# Contains code like `"x#$%"` which looks like bare interpolation but $% is not a valid global.
276+
# This confuses ripper, it emits two string tokens.
277+
"faker-3.1.1/lib/faker/default/internet.rb",
278+
"ruby_parser-3.20.0/test/test_ruby_parser.rb",
279+
"rouge-4.1.0/lib/rouge/lexers/cisco_ios.rb",
280+
"rouge-4.1.0/lib/rouge/lexers/ghc_cmm.rb",
281+
"rouge-4.1.0/lib/rouge/lexers/nasm.rb",
282+
"rouge-4.1.0/lib/rouge/lexers/velocity.rb",
283+
]
268284

269285
namespace :download do
270286
directory TOP_100_GEMS_DIR
@@ -346,7 +362,10 @@ task "lex:topgems": ["download:topgems", :compile] do
346362
lex_task.compare(filepath)
347363
end
348364

349-
gem_failing_files = lex_task.failing_files.map { |todo| todo.delete_prefix("#{directory}/") }
365+
gem_failing_files = lex_task.failing_files.filter_map do |todo|
366+
next if TOP_100_GEMS_LEX_RIPPER_BUG.any? { |path| todo.end_with?(path) }
367+
todo.delete_prefix("#{directory}/")
368+
end
350369
failing_files[gem_name] = gem_failing_files if gem_failing_files.any?
351370
end
352371

test/prism/bom_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
return if RUBY_ENGINE != "ruby"
66

77
require_relative "test_helper"
8+
require "ripper"
89

910
module Prism
1011
class BOMTest < TestCase
@@ -53,7 +54,7 @@ def test_string
5354

5455
def assert_bom(source)
5556
bommed = "\xEF\xBB\xBF#{source}"
56-
assert_equal Prism.lex_ripper(bommed), Prism.lex_compat(bommed).value
57+
assert_equal Ripper.lex(bommed), Prism.lex_compat(bommed).value
5758
end
5859
end
5960
end

test/prism/lex_test.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
return if !(RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.2.0")
44

55
require_relative "test_helper"
6+
require "ripper"
67

78
module Prism
89
class LexTest < TestCase
@@ -49,9 +50,19 @@ def test_parse_lex_file
4950
if RUBY_VERSION >= "3.3"
5051
def test_lex_compare
5152
prism = Prism.lex_compat(File.read(__FILE__), version: "current").value
52-
ripper = Prism.lex_ripper(File.read(__FILE__))
53+
ripper = Ripper.lex(File.read(__FILE__))
5354
assert_equal(ripper, prism)
5455
end
5556
end
57+
58+
def test_lex_ripper_deprecated
59+
warnings = capture_warnings { Prism.lex_ripper("foo") }
60+
61+
assert_include(warnings, "#{__FILE__}:#{__LINE__ - 2}")
62+
assert_include(warnings, "`Prism.lex_ripper` is deprecated")
63+
64+
warnings = capture_warnings { Prism.lex_ripper("foo") }
65+
assert_empty(warnings)
66+
end
5667
end
5768
end

0 commit comments

Comments
 (0)