class RuboCop::Cop::Style::RedundantStringEscape
Checks for redundant escapes in string literals.
@example
# bad - no need to escape # without following {/$/@ "\#foo" # bad - no need to escape single quotes inside double quoted string "\'foo\'" # bad - heredocs are also checked for unnecessary escapes <<~STR \#foo \"foo\" STR # good "#foo" # good "\#{no_interpolation}" # good "'foo'" # good "foo\ bar" # good <<~STR #foo "foo" STR
Constants
- MSG
Public Instance Methods
on_str(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 44 def on_str(node) return if node.parent&.regexp_type? || node.parent&.xstr_type? str_contents_range = str_contents_range(node) return unless str_contents_range.source.include?('\\') each_match_range(str_contents_range, /(\\.)/) do |range| next if allowed_escape?(node, range.resize(3)) add_offense(range) do |corrector| corrector.remove_leading(range, 1) end end end
Private Instance Methods
allowed_escape?(node, range)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 80 def allowed_escape?(node, range) escaped = range.source[(1..-1)] # Inside a single-quoted string, escapes (except \\ and \') do not have special meaning, # and so are not redundant, as they are a literal backslash. return true if interpolation_not_enabled?(node) # Strictly speaking a few single-letter chars are currently unnecessary to "escape", e.g. # d, but enumerating them is rather difficult, and their behavior could change over time # with different versions of Ruby so that e.g. /\d/ != /d/ return true if /[\n\\[[:alnum:]]]/.match?(escaped[0]) return true if escaped[0] == ' ' && percent_array_literal?(node) # Allow #{foo}, #$foo, #@foo, and #@@foo for escaping local, global, instance and class # variable interpolations inside return true if /\A#[{$@]/.match?(escaped) return true if delimiter?(node, escaped[0]) false end
array_literal?(node, prefix)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 121 def array_literal?(node, prefix) if literal_in_interpolated_or_multiline_string?(node) array_literal?(node.parent, prefix) else node.parent&.array_type? && node.parent.source.start_with?(prefix) end end
begin_loc_present?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 75 def begin_loc_present?(node) # e.g. a __FILE__ literal has no begin loc so we can't query if it's nil node.loc.to_hash.key?(:begin) && !node.loc.begin.nil? end
delimiter?(node, char)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 155 def delimiter?(node, char) return false if heredoc?(node) if literal_in_interpolated_or_multiline_string?(node) || percent_array_literal?(node) return delimiter?(node.parent, char) end delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]] delimiters.include?(char) end
heredoc?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 151 def heredoc?(node) (node.str_type? || node.dstr_type?) && node.heredoc? end
heredoc_with_disabled_interpolation?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 141 def heredoc_with_disabled_interpolation?(node) if heredoc?(node) node.loc.expression.source.end_with?("'") elsif node.parent&.dstr_type? heredoc_with_disabled_interpolation?(node.parent) else false end end
interpolation_not_enabled?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 102 def interpolation_not_enabled?(node) single_quoted?(node) || percent_w_literal?(node) || percent_q_literal?(node) || heredoc_with_disabled_interpolation?(node) end
literal_in_interpolated_or_multiline_string?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 167 def literal_in_interpolated_or_multiline_string?(node) node.str_type? && !begin_loc_present?(node) && node.parent&.dstr_type? end
message(range)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 61 def message(range) format(MSG, char: range.source.chars.last) end
percent_array_literal?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 137 def percent_array_literal?(node) (percent_w_literal?(node) || percent_w_upper_literal?(node)) end
percent_q_literal?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 113 def percent_q_literal?(node) if literal_in_interpolated_or_multiline_string?(node) percent_q_literal?(node.parent) else node.source.start_with?('%q') end end
percent_w_literal?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 129 def percent_w_literal?(node) array_literal?(node, '%w') end
percent_w_upper_literal?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 133 def percent_w_upper_literal?(node) array_literal?(node, '%W') end
single_quoted?(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 109 def single_quoted?(node) delimiter?(node, "'") end
str_contents_range(node)
click to toggle source
# File lib/rubocop/cop/style/redundant_string_escape.rb, line 65 def str_contents_range(node) if heredoc?(node) node.loc.heredoc_body elsif begin_loc_present?(node) contents_range(node) else node.loc.expression end end