1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
# frozen_string_literal: true
module RuboCop
module Cop
module Gettext
# Ensure that gettext identifiers are statically defined and not
# interpolated, formatted, or concatenated.
#
# @example
#
# # bad
# _('Hi #{name}')
# _('Hi %{name}' % { name: 'Luki' })
# _(format('Hi %{name}', name: 'Luki'))
#
# # good
# _('Hi %{name}') % { name: 'Luki' }
# format(_('Hi %{name}', name: 'Luki'))
#
# # also good
# var = "Hi"
# _(var)
# _(some_method_call)
# _(CONST)
class StaticIdentifier < RuboCop::Cop::Base
MSG = 'Ensure to pass static strings to translation method `%{method_name}(...)`.'
# Number of parameters to check for translation methods.
PARAMETERS_TO_CHECK = {
_: 1,
s_: 1,
N_: 1,
n_: 2
}.freeze
# RuboCop-specific optimization for `on_send`.
RESTRICT_ON_SEND = PARAMETERS_TO_CHECK.keys.freeze
DENIED_METHOD_CALLS = %i[% format + concat].freeze
def on_send(node)
method_name = node.method_name
arguments = node.arguments
each_invalid_argument(method_name, arguments) do |argument_node|
message = format(MSG, method_name: method_name)
add_offense(argument_node || node, message: message)
end
end
private
def each_invalid_argument(method_name, argument_nodes)
number = PARAMETERS_TO_CHECK.fetch(method_name)
argument_nodes.take(number).each do |argument_node|
yield argument_node unless valid_argument?(argument_node)
end
end
def valid_argument?(node)
return false unless node
basic_type?(node) || multiline_string?(node) || allowed_method_call?(node)
end
def basic_type?(node)
node.str_type? || node.lvar_type? || node.const_type?
end
def multiline_string?(node)
node.dstr_type? && node.children.all?(&:str_type?)
end
def allowed_method_call?(node)
return false unless node.send_type?
!DENIED_METHOD_CALLS.include?(node.method_name)
end
end
end
end
end
|