class RuboCop::Cop::Style::FormatStringToken


redirect(‘foo/%{bar_id}’)
# good
@example IgnoredMethods: [redirect]
format(‘%06d’, 10)
# good
format(‘%s %s.’, ‘Hello’, ‘world’)
# bad
@example MaxUnannotatedPlaceholdersAllowed: 1 (default)
format(‘%<number>06d’, number: 10)
# good
format(‘%s %s.’, ‘Hello’, ‘world’)
format(‘%06d’, 10)
# bad
@example MaxUnannotatedPlaceholdersAllowed: 0
`MaxUnannotatedPlaceholdersAllowed`.
if the number of them is less than or equals to
It is allowed to contain unannotated token
format(‘%s’, ‘Hello’)
# good
format(‘%{greeting}’, greeting: ‘Hello’)
format(‘%<greeting>s’, greeting: ‘Hello’)
# bad
@example EnforcedStyle: unannotated
format(‘%{greeting}’, greeting: ‘Hello’)
# good
format(‘%s’, ‘Hello’)
format(‘%<greeting>s’, greeting: ‘Hello’)
# bad
@example EnforcedStyle: template
format(‘%<greeting>s’, greeting: ‘Hello’)
# good
format(‘%s’, ‘Hello’)
format(‘%{greeting}’, greeting: ‘Hello’)
# bad
@example EnforcedStyle: annotated (default)
This cop can be customized ignored methods with ‘IgnoredMethods`.
to encoded URLs or Date/Time formatting strings.
The reason is that unannotated format is very similar
`printf`, `sprintf`, `format`, `%`.
which are passed as arguments to those methods:
NOTE: `unannotated` style cop only works for strings
Use a consistent style for named format string tokens.

def allowed_unannotated?(detections)

def allowed_unannotated?(detections)
  return false unless detections.all? do |detected_sequence,|
                        detected_sequence.style == :unannotated
                      end
  return true if detections.size <= max_unannotated_placeholders_allowed
  detections.any? { |detected_sequence,| !correctable_sequence?(detected_sequence.type) }
end

def autocorrect_sequence(corrector, detected_sequence, token_range)

def autocorrect_sequence(corrector, detected_sequence, token_range)
  return if style == :unannotated
  name = detected_sequence.name
  return if name.nil?
  flags = detected_sequence.flags
  width = detected_sequence.width
  precision = detected_sequence.precision
  type = detected_sequence.style == :template ? 's' : detected_sequence.type
  correction = case style
               when :annotated then "%<#{name}>#{flags}#{width}#{precision}#{type}"
               when :template then "%#{flags}#{width}#{precision}{#{name}}"
               end
  corrector.replace(token_range, correction)
end

def check_sequence(detected_sequence, token_range)

def check_sequence(detected_sequence, token_range)
  if detected_sequence.style == style
    correct_style_detected
  elsif correctable_sequence?(detected_sequence.type)
    style_detected(detected_sequence.style)
    add_offense(token_range, message: message(detected_sequence.style)) do |corrector|
      autocorrect_sequence(corrector, detected_sequence, token_range)
    end
  end
end

def collect_detections(node)

def collect_detections(node)
  detections = []
  tokens(node) do |detected_sequence, token_range|
    unless unannotated_format?(node, detected_sequence.style)
      detections << [detected_sequence, token_range]
    end
  end
  detections
end

def correctable_sequence?(detected_type)

def correctable_sequence?(detected_type)
  detected_type == 's' || style == :annotated || style == :unannotated
end

def format_string_token?(node)

def format_string_token?(node)
  !node.value.include?('%') || node.each_ancestor(:xstr, :regexp).any?
end

def max_unannotated_placeholders_allowed

def max_unannotated_placeholders_allowed
  cop_config['MaxUnannotatedPlaceholdersAllowed']
end

def message(detected_style)

def message(detected_style)
  "Prefer #{message_text(style)} over #{message_text(detected_style)}."
end

def message_text(style)

rubocop:disable Style/FormatStringToken
def message_text(style)
  {
    annotated: 'annotated tokens (like `%<foo>s`)',
    template: 'template tokens (like `%{foo}`)',
    unannotated: 'unannotated tokens (like `%s`)'
  }[style]
end

def on_str(node)

def on_str(node)
  return if format_string_token?(node) || use_ignored_method?(node)
  detections = collect_detections(node)
  return if detections.empty?
  return if allowed_unannotated?(detections)
  detections.each do |detected_sequence, token_range|
    check_sequence(detected_sequence, token_range)
  end
end

def str_contents(source_map)

def str_contents(source_map)
  if source_map.is_a?(Parser::Source::Map::Heredoc)
    source_map.heredoc_body
  elsif source_map.begin
    source_map.expression.adjust(begin_pos: +1, end_pos: -1)
  else
    source_map.expression
  end
end

def token_ranges(contents)

def token_ranges(contents)
  format_string = RuboCop::Cop::Utils::FormatString.new(contents.source)
  format_string.format_sequences.each do |detected_sequence|
    next if detected_sequence.percent?
    token = contents.begin.adjust(begin_pos: detected_sequence.begin_pos,
                                  end_pos: detected_sequence.end_pos)
    yield(detected_sequence, token)
  end
end

def tokens(str_node, &block)

def tokens(str_node, &block)
  return if str_node.source == '__FILE__'
  token_ranges(str_contents(str_node.loc), &block)
end

def unannotated_format?(node, detected_style)

def unannotated_format?(node, detected_style)
  detected_style == :unannotated && !format_string_in_typical_context?(node)
end

def use_ignored_method?(node)

def use_ignored_method?(node)
  send_parent = node.each_ancestor(:send).first
  send_parent && ignored_method?(send_parent.method_name)
end