lib/rubocop/cop/performance/string_replacement.rb in rubocop-0.45.0 vs lib/rubocop/cop/performance/string_replacement.rb in rubocop-0.46.0

- old
+ new

@@ -19,34 +19,36 @@ # 'abc'.tr('b', 'd') # 'a b c'.delete(' ') class StringReplacement < Cop MSG = 'Use `%s` instead of `%s`.'.freeze DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/ - REGEXP_CONSTRUCTOR_METHODS = [:new, :compile].freeze - GSUB_METHODS = [:gsub, :gsub!].freeze - DETERMINISTIC_TYPES = [:regexp, :str, :send].freeze DELETE = 'delete'.freeze TR = 'tr'.freeze BANG = '!'.freeze SINGLE_QUOTE = "'".freeze + def_node_matcher :string_replacement?, <<-PATTERN + (send _ ${:gsub :gsub!} + ${regexp str (send (const nil :Regexp) {:new :compile} _)} + $str) + PATTERN + def on_send(node) - _string, method, first_param, second_param = *node + string_replacement?(node) do |method, first_param, second_param| + return if accept_second_param?(second_param) + return if accept_first_param?(first_param) - return unless GSUB_METHODS.include?(method) - return if accept_second_param?(second_param) - return if accept_first_param?(first_param) - - offense(node, method, first_param, second_param) + offense(node, method, first_param, second_param) + end end def autocorrect(node) _string, method, first_param, second_param = *node first_source, = first_source(first_param) second_source, = *second_param - if regex?(first_param) + unless first_param.str_type? first_source = interpret_string_escapes(first_source) end replacement_method = replacement_method(method, first_source, second_source) @@ -70,23 +72,19 @@ end private def accept_second_param?(second_param) - return true unless string?(second_param) second_source, = *second_param - second_source.length > 1 end def accept_first_param?(first_param) - return true unless DETERMINISTIC_TYPES.include?(first_param.type) - first_source, options = first_source(first_param) return true if first_source.nil? - if regex?(first_param) + unless first_param.str_type? return true if options return true unless first_source =~ DETERMINISTIC_REGEX # This must be done after checking DETERMINISTIC_REGEX # Otherwise things like \s will trip us up first_source = interpret_string_escapes(first_source) @@ -95,41 +93,27 @@ first_source.length != 1 end def offense(node, method, first_param, second_param) first_source, = first_source(first_param) - if regex?(first_param) + unless first_param.str_type? first_source = interpret_string_escapes(first_source) end second_source, = *second_param message = message(method, first_source, second_source) add_offense(node, range(node), message) end - def string?(node) - node && node.str_type? - end - def first_source(first_param) case first_param.type - when :regexp, :send - return nil unless regex?(first_param) - source, options = extract_source(first_param) - when :str - source, = *first_param - end - - [source, options] - end - - def extract_source(node) - case node.type when :regexp - source_from_regex_literal(node) + source_from_regex_literal(first_param) when :send - source_from_regex_constructor(node) + source_from_regex_constructor(first_param) + when :str + first_param.children.first end end def source_from_regex_literal(node) regex, options = *node @@ -145,18 +129,9 @@ source_from_regex_literal(regex) when :str source, = *regex source end - end - - def regex?(node) - return true if node.regexp_type? - - const, init, = *node - _, klass = *const - - klass == :Regexp && REGEXP_CONSTRUCTOR_METHODS.include?(init) end def range(node) range_between(node.loc.selector.begin_pos, node.source_range.end_pos) end