class RuboCop::Cop::Lint::RedundantRegexpQuantifiers
Checks for redundant quantifiers inside Regexp literals.
It is always allowed when interpolation is used in a regexp literal, because it’s unknown what kind of string will be expanded as a result:
- source,ruby
/(?:a*#{interpolation})?/x
@example
# bad /(?:x+)+/ # good /(?:x)+/ # good /(?:x+)/ # bad /(?:x+)?/ # good /(?:x)*/ # good /(?:x*)/
Constants
- MSG_REDUNDANT_QUANTIFIER
Public Instance Methods
on_regexp(node)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 42 def on_regexp(node) return if node.interpolation? each_redundantly_quantified_pair(node) do |group, child| replacement = merged_quantifier(group, child) add_offense( quantifier_range(group, child), message: message(group, child, replacement) ) do |corrector| # drop outer quantifier corrector.replace(group.loc.quantifier, '') # replace inner quantifier corrector.replace(child.loc.quantifier, replacement) end end end
Private Instance Methods
character_set?(expr)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 83 def character_set?(expr) expr.is?(:character, :set) end
each_redundantly_quantified_pair(node) { |expr, subexp| ... }
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 61 def each_redundantly_quantified_pair(node) seen = Set.new node.parsed_tree&.each_expression do |(expr)| next if seen.include?(expr) || !redundant_group?(expr) || !mergeable_quantifier(expr) expr.each_expression do |(subexp)| seen << subexp break unless redundantly_quantifiable?(subexp) yield(expr, subexp) if mergeable_quantifier(subexp) end end end
mergeable_quantifier(expr)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 87 def mergeable_quantifier(expr) # Merging reluctant or possessive quantifiers would be more complex, # and Ruby does not emit warnings for these cases. return unless expr.quantifier&.greedy? # normalize quantifiers, e.g. "{1,}" => "+" case expr.quantity when [0, -1] '*' when [0, 1] '?' when [1, -1] '+' end end
merged_quantifier(exp1, exp2)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 103 def merged_quantifier(exp1, exp2) quantifier1 = mergeable_quantifier(exp1) quantifier2 = mergeable_quantifier(exp2) if quantifier1 == quantifier2 # (?:a+)+ equals (?:a+) ; (?:a*)* equals (?:a*) ; # (?:a?)? equals (?:a?) quantifier1 else # (?:a+)*, (?:a+)?, (?:a*)+, (?:a*)?, (?:a?)+, (?:a?)* - all equal (?:a*) '*' end end
message(group, child, replacement)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 119 def message(group, child, replacement) format( MSG_REDUNDANT_QUANTIFIER, inner_quantifier: child.quantifier.to_s, outer_quantifier: group.quantifier.to_s, replacement: replacement ) end
quantifier_range(group, child)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 115 def quantifier_range(group, child) range_between(child.loc.quantifier.begin_pos, group.loc.quantifier.end_pos) end
redundant_group?(expr)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 75 def redundant_group?(expr) expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1 end
redundantly_quantifiable?(node)
click to toggle source
# File lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb, line 79 def redundantly_quantifiable?(node) redundant_group?(node) || character_set?(node) || node.terminal? end