Skip to content

Commit fba491e

Browse files
authored
Fix methods defined with invalid encoding are not displayed in completion (#1101)
* Fix methods defined with invalid encoding are not displayed in completion * Ignore invalid encoding in completion data retrieval * Handle Encoding::UndefinedConversionError gracefully in completion candidates * Replace map and compact with filter_map
1 parent fe2b1ea commit fba491e

3 files changed

Lines changed: 66 additions & 2 deletions

File tree

lib/irb/completion.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,14 @@ def completion_candidates(preposing, target, _postposing, bind:)
107107

108108
return commands unless result
109109

110-
commands | result.completion_candidates.map { target + _1 }
110+
encoded_candidates = result.completion_candidates.filter_map do |i|
111+
encoded = i.encode(Encoding.default_external)
112+
target + encoded
113+
rescue Encoding::UndefinedConversionError
114+
# If the string cannot be converted, we just ignore it
115+
nil
116+
end
117+
commands | encoded_candidates
111118
end
112119

113120
def doc_namespace(preposing, matched, _postposing, bind:)
@@ -194,7 +201,12 @@ def completion_candidates(preposing, target, postposing, bind:)
194201
# It doesn't make sense to propose commands with other preposing
195202
commands = [] unless preposing.empty?
196203

197-
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
204+
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.filter_map do |i|
205+
i.encode(Encoding.default_external)
206+
rescue Encoding::UndefinedConversionError
207+
# If the string cannot be converted, we just ignore it
208+
nil
209+
end
198210
commands | completion_data
199211
end
200212

test/irb/test_completion.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,34 @@ def test_retrieve_completion_data
314314
assert_equal(IRB::InputCompletor.retrieve_completion_data('a.abs', bind: bind, doc_namespace: true), 'Integer.abs')
315315
end
316316
end
317+
318+
class InvalidEncodingMethodTest < TestCase
319+
def test_regexp_completor_handles_encoding_errors_gracefully
320+
if RUBY_ENGINE == 'truffleruby'
321+
omit "TruffleRuby does not support invalid encoding methods."
322+
end
323+
324+
invalid_method_name = "b\xff".force_encoding(Encoding::ASCII_8BIT)
325+
326+
test_obj = Object.new
327+
test_obj.define_singleton_method(invalid_method_name) {}
328+
test_bind = test_obj.instance_eval { binding }
329+
330+
original_encoding = Encoding.default_external
331+
332+
begin
333+
Encoding.default_external = Encoding::UTF_8
334+
335+
completor = IRB::RegexpCompletor.new
336+
337+
assert_nothing_raised do
338+
result = completor.completion_candidates('', 'b', '', bind: test_bind)
339+
assert_include(result, 'block_given?')
340+
assert_not_include(result, nil)
341+
end
342+
ensure
343+
Encoding.default_external = original_encoding
344+
end
345+
end
346+
end
317347
end

test/irb/test_type_completor.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,28 @@ def test_command_completion
8080
assert_include(@completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
8181
assert_not_include(@completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
8282
end
83+
84+
def test_type_completor_handles_encoding_errors_gracefully
85+
invalid_method_name = "b\xff".dup.force_encoding(Encoding::ASCII_8BIT)
86+
87+
test_obj = Object.new
88+
test_obj.define_singleton_method(invalid_method_name) {}
89+
test_bind = test_obj.instance_eval { binding }
90+
91+
original_encoding = Encoding.default_external
92+
93+
begin
94+
Encoding.default_external = Encoding::UTF_8
95+
96+
assert_nothing_raised do
97+
result = @completor.completion_candidates('', 'b', '', bind: test_bind)
98+
assert_include(result, 'block_given?')
99+
assert_not_include(result, nil)
100+
end
101+
ensure
102+
Encoding.default_external = original_encoding
103+
end
104+
end
83105
end
84106

85107
class TypeCompletorIntegrationTest < IntegrationTestCase

0 commit comments

Comments
 (0)