class RuboCop::Cop::Style::FormatStringToken
Use a consistent style for named format string tokens.
*Note:* `unannotated` style cop only works for strings which are passed as arguments to those methods: `sprintf`, `format`, `%`. The reason is that unannotated format is very similar to encoded URLs or Date/Time formatting strings.
@example EnforcedStyle: annotated (default)
# bad format('%{greeting}', greeting: 'Hello') format('%s', 'Hello') # good format('%<greeting>s', greeting: 'Hello')
@example EnforcedStyle: template
# bad format('%<greeting>s', greeting: 'Hello') format('%s', 'Hello') # good format('%{greeting}', greeting: 'Hello')
@example EnforcedStyle: unannotated
# bad format('%<greeting>s', greeting: 'Hello') format('%{greeting}', 'Hello') # good format('%s', 'Hello')
Constants
- FIELD_CHARACTERS
- FORMAT_STRING_METHODS
- STYLE_PATTERNS
Public Instance Methods
on_str(node)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 53 def on_str(node) return if placeholder_argument?(node) return if node.each_ancestor(:xstr, :regexp).any? tokens(node) do |detected_style, token_range| if detected_style == style || unannotated_format?(node, detected_style) correct_style_detected else style_detected(detected_style) add_offense(node, location: token_range, message: message(detected_style)) end end end
Private Instance Methods
includes_format_methods?(node)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 71 def includes_format_methods?(node) node.each_ancestor(:send).any? do |ancestor| FORMAT_STRING_METHODS.include?(ancestor.method_name) end end
match_token(source_range)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 123 def match_token(source_range) supported_styles.each do |style_name| pattern = STYLE_PATTERNS.fetch(style_name) match = source_range.source.match(pattern) next unless match return [style_name, match.begin(:token), match.end(:token)] end nil end
message(detected_style)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 81 def message(detected_style) "Prefer #{message_text(style)} over #{message_text(detected_style)}." end
message_text(style)
click to toggle source
rubocop:disable Style/FormatStringToken
# File lib/rubocop/cop/style/format_string_token.rb, line 86 def message_text(style) case style when :annotated then 'annotated tokens (like `%<foo>s`)' when :template then 'template tokens (like `%{foo}`)' when :unannotated then 'unannotated tokens (like `%s`)' end end
placeholder_argument?(node)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 161 def placeholder_argument?(node) return false unless node.parent return true if node.parent.pair_type? placeholder_argument?(node.parent) end
slice_source(source_range, new_begin, new_end)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 153 def slice_source(source_range, new_begin, new_end) Parser::Source::Range.new( source_range.source_buffer, new_begin, new_end ) end
split_token(source_range, match_begin, match_end)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 135 def split_token(source_range, match_begin, match_end) token = slice_source( source_range, source_range.begin_pos + match_begin, source_range.begin_pos + match_end ) remainder = slice_source( source_range, source_range.begin_pos + match_end, source_range.end_pos ) [token, remainder] end
str_contents(source_map)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 101 def str_contents(source_map) if source_map.is_a?(Parser::Source::Map::Heredoc) source_map.heredoc_body elsif source_map.begin slice_source( source_map.expression, source_map.expression.begin_pos + 1, source_map.expression.end_pos - 1 ) else source_map.expression end end
token_ranges(contents) { |detected_style, token| ... }
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 115 def token_ranges(contents) while (offending_match = match_token(contents)) detected_style, *range = *offending_match token, contents = split_token(contents, *range) yield(detected_style, token) end end
tokens(str_node, &block)
click to toggle source
rubocop:enable Style/FormatStringToken
# File lib/rubocop/cop/style/format_string_token.rb, line 95 def tokens(str_node, &block) return if str_node.source == '__FILE__' token_ranges(str_contents(str_node.loc), &block) end
unannotated_format?(node, detected_style)
click to toggle source
# File lib/rubocop/cop/style/format_string_token.rb, line 77 def unannotated_format?(node, detected_style) detected_style == :unannotated && !includes_format_methods?(node) end