Skip to content

Rails/UniqBeforePluck: False positive in conservative mode for non-model constants #1568

@tim-gq

Description

@tim-gq

Expected behavior

In conservative mode, Rails/UniqBeforePluck should only flag calls to pluck on model class constants (e.g., User, Post), not on arbitrary constants that happen to be arrays or other non-ActiveRecord objects.

From the documentation:

When the EnforcedStyle is conservative (the default) then only calls to pluck on a constant (i.e. a model class) before uniq are added as offenses.

Actual behavior

The cop flags any constant, including plain Ruby array constants. The autocorrection then produces broken code because distinct is an ActiveRecord method that doesn't exist on plain arrays.

Steps to reproduce the problem

# example.rb
DEFAULT_RULES = [
  { resource_type: 'Study', action: :read },
  { resource_type: 'Study', action: :write },
  { resource_type: 'Candidate', action: :read }
].freeze

resource_types = DEFAULT_RULES.pluck(:resource_type).uniq

Run with:

rubocop --only Rails/UniqBeforePluck example.rb

Output:

example.rb:8:50: C: Rails/UniqBeforePluck: Use distinct before pluck.
resource_types = DEFAULT_RULES.pluck(:resource_type).uniq
                                                     ^^^^

After autocorrection (rubocop -A):

resource_types = DEFAULT_RULES.distinct.pluck(:resource_type)

This code fails at runtime with:

NoMethodError: undefined method 'distinct' for an instance of Array

Root cause

The conservative mode check in the cop uses:

pluck_node.receiver&.const_type?

This returns true for any constant, not just model classes. The cop cannot distinguish between:

  • User (a model class where distinct is valid)
  • DEFAULT_RULES (an Array constant where distinct doesn't exist)

Suggested fix

Either:

  1. Update the documentation to reflect that conservative mode flags all constants (not just model classes)
  2. Or improve the detection to only flag constants that are likely ActiveRecord models (e.g., by checking inheritance or using heuristics)
  3. Or mark SafeAutoCorrect: false is already set, but consider adding a note that this autocorrection can produce broken code for non-AR constants

RuboCop version

$ bundle exec rubocop -V
1.81.7 (using Parser 3.3.10.0, Prism 1.6.0, rubocop-ast 1.48.0, analyzing as Ruby 3.4, running on ruby 3.4.7) [arm64-darwin24]
  - rubocop-rails 2.34.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions