rubocop / rubocop-performance

An extension of RuboCop focused on code performance checks.
https://docs.rubocop.org/rubocop-performance
MIT License
682 stars 80 forks source link

Autocorrection for Performance/ConstantRegexp is not working with pattern matching #438

Open morissetcl opened 9 months ago

morissetcl commented 9 months ago

Expected behavior

The autocorrection for Performance/ConstantRegexp should fix the offense even with pattern matching.

Actual behavior

The autocorrection for Performance/ConstantRegexp prepends the end delimiter /o, leading to the following error:

NODE_IN: unknown node (NODE_ONCE)

Steps to reproduce the problem

I have a method that uses pattern matching:

# constants/errors.rb
UNINITIALIZED_CONSTANT = "uninitialized constant".freeze
UNDEFINED_VARIABLE = "undefined local variable".freeze

def call
  case exception.message
  in /#{Constants::Errors::UNINITIALIZED_CONSTANT}/
    # do something
  in /#{Constants::Errors::UNDEFINED_VARIABLE}/
    # do something
  else
    # do something
  end
end

It works well but rubocop-performance raises the following offense:

Performance/ConstantRegexp: Extract this regexp into a constant, memoize it, or append an `/o` option to its options

The autocorrect prepends /o

# constants/errors.rb
UNINITIALIZED_CONSTANT = "uninitialized constant".freeze
UNDEFINED_VARIABLE = "undefined local variable".freeze

def call
  case exception.message
  in /#{Constants::Errors::UNINITIALIZED_CONSTANT}/o
    # do something
  in /#{Constants::Errors::UNDEFINED_VARIABLE}/o
    # do something
  else
    # do something
  end
end

and it breaks the code with the following error:

NODE_IN: unknown node (NODE_ONCE)

FYI I tried an if/else version and it works correctly

def call
  if exception.message.match?(/#{Constants::Errors::UNINITIALIZED_CONSTANT}/o)
    # do something
  elsif exception.message.match?(/#{Constants::Errors::UNDEFINED_VARIABLE}/o)
    # do something
  else
    # do something
  end
end

RuboCop version

rubocop (1.57.2)
rubocop-performance (1.19.1)

Note: I use the standard gem

koic commented 9 months ago

Can you provide an executable code and steps to reproduce the NODE_IN: unknown node (NODE_ONCE) error?

morissetcl commented 9 months ago

Yes sure:

1/ Run the code below ruby script.rb

class Script
  UNINITIALIZED_CONSTANT = "uninitialized constant".freeze
  UNDEFINED_VARIABLE = "undefined local variable".freeze

  class Error
    def self.call(exception)
      case exception
      in /#{UNINITIALIZED_CONSTANT}/
        p "UNINITIALIZED_CONSTANT"
      in /#{UNDEFINED_VARIABLE}/
        p "UNDEFINED_VARIABLE"
      else
        # noop
      end
    end
  end

  Error.call(UNINITIALIZED_CONSTANT)
end

2/ Run rubocop (as mentioned I use standard)

output:

script.rb:9:10: C: [Correctable] Performance/ConstantRegexp: Extract this regexp into a constant, memoize it, or append an /o option to its options.
      in /#{UNINITIALIZED_CONSTANT}/
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
script.rb:11:10: C: [Correctable] Performance/ConstantRegexp: Extract this regexp into a constant, memoize it, or append an /o option to its options.
      in /#{UNDEFINED_VARIABLE}/

3/ Run the autocorrect command

output:

script.rb: script.rb:5: NODE_IN: unknown node (NODE_ONCE) (SyntaxError)

Thanks.