Skip to content

Commit

Permalink
[#11076] Add autocorrect for hash in Lint/LiteralInInterpolation (#…
Browse files Browse the repository at this point in the history
…11475)

This commit also refactors the cop's unit tests, which were a bit messy.
  • Loading branch information
KessaPassa committed Feb 27, 2023
1 parent 999ac27 commit cde4bc2
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#11475](https://github.com/rubocop/rubocop/pull/11475): Add autocorrect for hash in `Lint/LiteralInInterpolation`. ([@KessaPassa][])
48 changes: 45 additions & 3 deletions lib/rubocop/cop/lint/literal_in_interpolation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def special_keyword?(node)
(node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__')
end

# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
def autocorrected_value(node)
case node.type
when :int
Expand All @@ -71,13 +71,15 @@ def autocorrected_value(node)
autocorrected_value_for_symbol(node)
when :array
autocorrected_value_for_array(node)
when :hash
autocorrected_value_for_hash(node)
when :nil
''
else
node.source.gsub('"', '\"')
end
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity

def autocorrected_value_for_string(node)
if node.source.start_with?("'", '%q')
Expand All @@ -91,7 +93,16 @@ def autocorrected_value_for_symbol(node)
end_pos =
node.loc.end ? node.loc.end.begin_pos : node.source_range.end_pos

range_between(node.loc.begin.end_pos, end_pos).source
range_between(node.loc.begin.end_pos, end_pos).source.gsub('"', '\"')
end

def autocorrected_value_in_hash_for_symbol(node)
# TODO: We need to detect symbol unacceptable names more correctly
if / |"|'/.match?(node.value.to_s)
":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
else
":#{node.value}"
end
end

def autocorrected_value_for_array(node)
Expand All @@ -100,6 +111,37 @@ def autocorrected_value_for_array(node)
contents_range(node).source.split.to_s.gsub('"', '\"')
end

def autocorrected_value_for_hash(node)
hash_string = node.children.map do |child|
key = autocorrected_value_in_hash(child.key)
value = autocorrected_value_in_hash(child.value)
"#{key}=>#{value}"
end.join(', ')

"{#{hash_string}}"
end

# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def autocorrected_value_in_hash(node)
case node.type
when :int
node.children.last.to_i.to_s
when :float
node.children.last.to_f.to_s
when :str
"\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
when :sym
autocorrected_value_in_hash_for_symbol(node)
when :array
autocorrected_value_for_array(node)
when :hash
autocorrected_value_for_hash(node)
else
node.source.gsub('"', '\"')
end
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize

# Does node print its own source when converted to a string?
def prints_as_self?(node)
node.basic_literal? ||
Expand Down
132 changes: 107 additions & 25 deletions spec/rubocop/cop/lint/literal_in_interpolation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,31 +89,113 @@
end
end

it_behaves_like('literal interpolation', 1)
it_behaves_like('literal interpolation', -1)
it_behaves_like('literal interpolation', '1_123', '1123')
it_behaves_like('literal interpolation', '123_456_789_123_456_789', '123456789123456789')
it_behaves_like('literal interpolation', '1.2e-3', '0.0012')
it_behaves_like('literal interpolation', '0xaabb', '43707')
it_behaves_like('literal interpolation', '0o377', '255')
it_behaves_like('literal interpolation', 2.0)
it_behaves_like('literal interpolation', '[]', '[]')
it_behaves_like('literal interpolation', '["a", "b"]', '[\"a\", \"b\"]')
it_behaves_like('literal interpolation', '{"a" => "b"}', '{\"a\" => \"b\"}')
it_behaves_like('literal interpolation', true)
it_behaves_like('literal interpolation', false)
it_behaves_like('literal interpolation', 'nil', '')
it_behaves_like('literal interpolation', ':symbol', 'symbol')
it_behaves_like('literal interpolation', ':"symbol"', 'symbol')
it_behaves_like('literal interpolation', 1..2)
it_behaves_like('literal interpolation', 1...2)
it_behaves_like('literal interpolation', '%w[]', '[]')
it_behaves_like('literal interpolation', '%w[v1]', '[\"v1\"]')
it_behaves_like('literal interpolation', '%w[v1 v2]', '[\"v1\", \"v2\"]')
it_behaves_like('literal interpolation', '%i[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%I[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%i[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%i[ s1 s2 ]', '[\"s1\", \"s2\"]')
describe 'type int' do
it_behaves_like('literal interpolation', 1)
it_behaves_like('literal interpolation', -1)
it_behaves_like('literal interpolation', '1_123', '1123')
it_behaves_like('literal interpolation', '123_456_789_123_456_789', '123456789123456789')
it_behaves_like('literal interpolation', '0xaabb', '43707')
it_behaves_like('literal interpolation', '0o377', '255')
end

describe 'type float' do
it_behaves_like('literal interpolation', '1.2e-3', '0.0012')
it_behaves_like('literal interpolation', 2.0)
end

describe 'type str' do
it_behaves_like('literal interpolation', '"double_quot_string"', 'double_quot_string')
it_behaves_like('literal interpolation', "'single_quot_string'", 'single_quot_string')
it_behaves_like('literal interpolation', '"double_quot_string: \'"', "double_quot_string: '")
it_behaves_like('literal interpolation', "'single_quot_string: \"'", 'single_quot_string: \"')
end

describe 'type sym' do
it_behaves_like('literal interpolation', ':symbol', 'symbol')
it_behaves_like('literal interpolation', ':"symbol"', 'symbol')
it_behaves_like('literal interpolation',
':"single quot in symbol: \'"', "single quot in symbol: '")
it_behaves_like('literal interpolation',
":'double quot in symbol: \"'", 'double quot in symbol: \"')
end

describe 'type array' do
it_behaves_like('literal interpolation', '[]', '[]')
it_behaves_like('literal interpolation', '["a", "b"]', '[\"a\", \"b\"]')
it_behaves_like('literal interpolation', '%w[]', '[]')
it_behaves_like('literal interpolation', '%w[v1]', '[\"v1\"]')
it_behaves_like('literal interpolation', '%w[v1 v2]', '[\"v1\", \"v2\"]')
it_behaves_like('literal interpolation', '%i[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%I[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%i[s1 s2]', '[\"s1\", \"s2\"]')
it_behaves_like('literal interpolation', '%i[ s1 s2 ]', '[\"s1\", \"s2\"]')
end

describe 'type hash' do
it_behaves_like('literal interpolation', '{"a" => "b"}', '{\"a\"=>\"b\"}')
it_behaves_like('literal interpolation', "{ foo: 'bar', :fiz => \"buzz\" }",
'{:foo=>\"bar\", :fiz=>\"buzz\"}')
it_behaves_like('literal interpolation', "{ foo: { fiz: 'buzz' } }", '{:foo=>{:fiz=>\"buzz\"}}')
it_behaves_like(
'literal interpolation',
'{ num: { separate: 1_123, long_separate: 123_456_789_123_456_789, exponent: 1.2e-3 } }',
'{:num=>{:separate=>1123, :long_separate=>123456789123456789, :exponent=>0.0012}}'
)
it_behaves_like('literal interpolation', '{ n_adic_num: { hex: 0xaabb, oct: 0o377 } }',
'{:n_adic_num=>{:hex=>43707, :oct=>255}}')
it_behaves_like(
'literal interpolation',
'{ double_quot: { simple: "double_quot", single_in_double: "double_quot: \'" } }',
'{:double_quot=>{:simple=>\"double_quot\", :single_in_double=>\"double_quot: \'\"}}'
)
it_behaves_like(
'literal interpolation',
"{ single_quot: { simple: 'single_quot', double_in_single: 'single_quot: \"' } }",
'{:single_quot=>{:simple=>\"single_quot\", :double_in_single=>\"single_quot: \\\\\\"\"}}'
)
it_behaves_like('literal interpolation', '{ bool: { key: true } }', '{:bool=>{:key=>true}}')
it_behaves_like('literal interpolation', '{ bool: { key: false } }', '{:bool=>{:key=>false}}')
it_behaves_like('literal interpolation', '{ nil: { key: nil } }', '{:nil=>{:key=>nil}}')
it_behaves_like('literal interpolation', '{ symbol: { key: :symbol } }',
'{:symbol=>{:key=>:symbol}}')
it_behaves_like('literal interpolation', '{ symbol: { key: :"symbol" } }',
'{:symbol=>{:key=>:symbol}}')
it_behaves_like('literal interpolation',
'{ single_quot_symbol: { key: :"single_quot_in_symbol: \'" } }',
'{:single_quot_symbol=>{:key=>:\"single_quot_in_symbol: \'\"}}')
it_behaves_like('literal interpolation',
"{ double_quot_symbol: { key: :'double_quot_in_symbol: \"' } }",
'{:double_quot_symbol=>{:key=>:\"double_quot_in_symbol: \\\\\"\"}}')
it_behaves_like('literal interpolation',
'{ single_quot_symbol_not_in_space: { key: :"single_quot_in_symbol:\'" } }',
'{:single_quot_symbol_not_in_space=>{:key=>:\"single_quot_in_symbol:\'\"}}')
it_behaves_like('literal interpolation',
'{ single_quot_symbol_in_space: { key: :"single_quot_in_symbol: " } }',
'{:single_quot_symbol_in_space=>{:key=>:\"single_quot_in_symbol: \"}}')
it_behaves_like('literal interpolation', '{ range: { key: 1..2 } }', '{:range=>{:key=>1..2}}')
it_behaves_like('literal interpolation', '{ range: { key: 1...2 } }', '{:range=>{:key=>1...2}}')
it_behaves_like('literal interpolation', '{ array: { key: %w[] } }', '{:array=>{:key=>[]}}')
it_behaves_like('literal interpolation', '{ array: { key: %w[v1] } }',
'{:array=>{:key=>[\"v1\"]}}')
it_behaves_like('literal interpolation', '{ array: { key: %w[v1 v2] } }',
'{:array=>{:key=>[\"v1\", \"v2\"]}}')
it_behaves_like('literal interpolation', '{ array: { key: %i[s1 s2] } }',
'{:array=>{:key=>[\"s1\", \"s2\"]}}')
it_behaves_like('literal interpolation', '{ array: { key: %I[s1 s2] } }',
'{:array=>{:key=>[\"s1\", \"s2\"]}}')
it_behaves_like('literal interpolation', '{ array: { key: %i[s1 s2] } }',
'{:array=>{:key=>[\"s1\", \"s2\"]}}')
it_behaves_like('literal interpolation', '{ array: { key: %i[ s1 s2 ] } }',
'{:array=>{:key=>[\"s1\", \"s2\"]}}')
end

describe 'type else' do
it_behaves_like('literal interpolation', 'nil', '')
it_behaves_like('literal interpolation', 1..2)
it_behaves_like('literal interpolation', 1...2)
it_behaves_like('literal interpolation', true)
it_behaves_like('literal interpolation', false)
end

shared_examples 'literal interpolation in words literal' do |prefix|
let(:word) { 'interpolation' }
Expand Down

0 comments on commit cde4bc2

Please sign in to comment.