class RuboCop::Cop::Lint::SymbolConversion
}
b: 2
a: 1,
{
# good (no quoting required)
}
‘b-c’: 2
’a’: 1,
{
# good (quote all keys if any need quoting)
}
‘b-c’: 2
a: 1,
{
# bad
@example EnforcedStyle: consistent
}
‘c-d’: 3
b: 2,
a: 1,
{
# good (don’t quote keys that don’t require quoting)
}
‘c-d’: 3
“b”: 2,
‘a’: 1,
{
# bad
@example EnforcedStyle: strict (default)
:“string_#{interpolation}”
:‘hyphenated-string’
:underscored_symbol
:underscored_string
:symbol
:string
# good
“string_#{interpolation}”.to_sym
’hyphenated-string’.to_sym
:‘underscored_symbol’
‘underscored_string’.to_sym
:symbol.to_sym
’string’.to_sym
# bad
@example
all keys to be quoted).
every symbol key (ie. if any symbol key needs to be quoted it requires
`consistent` additionally requires hashes to use the same style for
`strict` (default) will register an offense for any incorrect usage.
There are two possible styles for this cop.
a symbol where a literal symbol could be used instead.
Checks for uses of literal strings converted to
def correct_hash_key(node)
def correct_hash_key(node) # Although some operators can be converted to symbols normally # (ie. `:==`), these are not accepted as hash keys and will # raise a syntax error (eg. `{ ==: ... }`). Therefore, if the # symbol does not start with an alphanumeric or underscore, it # will be ignored. return unless node.value.to_s.match?(/\A[a-z0-9_]/i) correction = node.value.inspect correction = correction.delete_prefix(':') if node.parent.colon? return if properly_quoted?(node.source, correction) register_offense( node, correction: correction, message: format(MSG, correction: node.parent.colon? ? "#{correction}:" : correction) ) end
def correct_inconsistent_hash_keys(keys)
def correct_inconsistent_hash_keys(keys) keys.each do |key| ignore_node(key) next if requires_quotes?(key) next if properly_quoted?(key.source, %("#{key.value}")) correction = %("#{key.value}") register_offense( key, correction: correction, message: format(MSG_CONSISTENCY, correction: "#{correction}:") ) end end
def in_alias?(node)
def in_alias?(node) node.parent&.alias_type? end
def in_percent_literal_array?(node)
def in_percent_literal_array?(node) node.parent&.array_type? && node.parent&.percent_literal? end
def on_hash(node)
def on_hash(node) # For `EnforcedStyle: strict`, hash keys are evaluated in `on_sym` return unless style == :consistent keys = node.keys.select(&:sym_type?) if keys.any? { |key| requires_quotes?(key) } correct_inconsistent_hash_keys(keys) else # If there are no symbol keys requiring quoting, # treat the hash like `EnforcedStyle: strict`. keys.each { |key| correct_hash_key(key) } end end
def on_send(node)
def on_send(node) return unless node.receiver if node.receiver.str_type? || node.receiver.sym_type? register_offense(node, correction: node.receiver.value.to_sym.inspect) elsif node.receiver.dstr_type? register_offense(node, correction: ":\"#{node.receiver.value.to_sym}\"") end end
def on_sym(node)
def on_sym(node) return if ignored_node?(node) || properly_quoted?(node.source, node.value.inspect) # `alias` arguments are symbols but since a symbol that requires # being quoted is not a valid method identifier, it can be ignored return if in_alias?(node) # The `%I[]` and `%i[]` macros are parsed as normal arrays of symbols # so they need to be ignored. return if in_percent_literal_array?(node) # Symbol hash keys have a different format and need to be handled separately return correct_hash_key(node) if hash_key?(node) register_offense(node, correction: node.value.inspect) end
def properly_quoted?(source, value)
def properly_quoted?(source, value) return true if style == :strict && (!source.match?(/['"]/) || value.end_with?('=')) source == value || # `Symbol#inspect` uses double quotes, but allow single-quoted # symbols to work as well. source.gsub('"', '\"').tr("'", '"') == value end
def register_offense(node, correction:, message: format(MSG, correction: correction))
def register_offense(node, correction:, message: format(MSG, correction: correction)) add_offense(node, message: message) { |corrector| corrector.replace(node, correction) } end
def requires_quotes?(sym_node)
def requires_quotes?(sym_node) sym_node.value.inspect.match?(/^:".*?"|=$/) end