Skip to content

[bot] Fix Rails/Blank#206

Closed
6[bot] wants to merge 1 commit intomainfrom
fix/rails-blank-23572694990
Closed

[bot] Fix Rails/Blank#206
6[bot] wants to merge 1 commit intomainfrom
fix/rails-blank-23572694990

Conversation

@6
Copy link
Copy Markdown
Contributor

@6 6 bot commented Mar 26, 2026

Status: Agent is working on this fix...

Cop: Rails/Blank | Backend: claude / hard | Model: Claude Opus 4.6 (high) | Mode: fix
Code bugs: 9 | Run: https://github.com/6/nitrocop/actions/runs/23572694990

Refs #160

Task prompt (4398 tokens)

Fix Rails/Blank — 0 FP, 14 FN

Instructions

You are fixing ONE cop in nitrocop, a Rust Ruby linter that uses Prism for parsing.

Current state: 6,472 matches, 0 false positives, 14 false negatives.
Focus on: FN (RuboCop flags code nitrocop misses).

⚠ 6,472 existing matches must not regress. Validate with check_cop.py before committing.

Workflow

  1. Read the Pre-diagnostic Results and Corpus FP/FN Examples sections below first
  2. Verify with RuboCop first (for FP fixes): before writing any code, confirm RuboCop's
    behavior on BOTH the specific FP case AND the general pattern:
    echo '<specific FP case>' > /tmp/test.rb && rubocop --only Rails/Blank /tmp/test.rb
    echo '<general pattern>' > /tmp/test.rb && rubocop --only Rails/Blank /tmp/test.rb
    If RuboCop flags the general pattern, your fix must be narrow enough to not suppress it.
  3. Add a test case FIRST:
    • FN fix: add the missed pattern to tests/fixtures/cops/rails/blank/offense.rb with ^ annotation
    • FP fix: add the false-positive pattern to tests/fixtures/cops/rails/blank/no_offense.rb
  4. Verify test fails: cargo test --lib -- cop::rails::blank
  5. Fix src/cop/rails/blank.rs
  6. Verify test passes: cargo test --lib -- cop::rails::blank
  7. Validate against corpus (REQUIRED before committing):
    python3 scripts/check_cop.py Rails/Blank --rerun --clone --sample 15
    If this reports FP or FN regression, your fix is too broad — narrow it down.
  8. Add a /// doc comment on the cop struct documenting what you found and fixed
  9. Commit only your cop's files

Fixture Format

Mark offenses with ^ markers on the line AFTER the offending source line.
The ^ characters must align with the offending columns. The message format is Rails/Blank: <message text>.
See the Current Fixture sections below for real examples from this cop.

If your test passes immediately

If you add a test case and it passes without code changes, the corpus mismatch is
caused by config/context differences, not a detection bug.
Do NOT loop trying to make the test fail. Instead:

  1. Investigate config resolution (Include/Exclude, cop enablement, disable comments)
  2. The fix is likely in src/config/ or the cop's config handling, not detection logic
  3. If you cannot determine the root cause within 5 minutes, document your findings as
    a /// comment on the cop struct and commit

CRITICAL: Avoid regressions in the opposite direction

When fixing FPs, your change MUST NOT suppress legitimate detections. When fixing FNs,
your change MUST NOT flag code that RuboCop accepts. A fix that eliminates a few issues
in one direction but introduces hundreds in the other is a catastrophic regression.

Before exempting a category of patterns, verify with RuboCop that the general case
is still an offense:

rubocop --only Rails/Blank /tmp/test.rb

If RuboCop flags the general pattern but not your specific case, the difference is in
a narrow context (e.g., enclosing structure, receiver type, argument count) — your fix
must target that specific context, not the broad category.

Rule of thumb: if your fix adds an early return or continue that skips a whole
node type, operator class, or naming pattern, it's probably too broad. Prefer adding a
condition that matches the SPECIFIC differentiating context.

Rules

  • Only modify src/cop/rails/blank.rs and tests/fixtures/cops/rails/blank/
  • Run cargo test --lib -- cop::rails::blank to verify your fix (do NOT run the full test suite)
  • Run python3 scripts/check_cop.py Rails/Blank --rerun --clone --sample 15 before committing to catch regressions
  • Do NOT touch unrelated files
  • Do NOT use git stash

Current Fixture: offense.rb

tests/fixtures/cops/rails/blank/offense.rb

!x.present?
^^^^^^^^^^^ Rails/Blank: Use `blank?` instead of `!present?`.

!name.present?
^^^^^^^^^^^^^^ Rails/Blank: Use `blank?` instead of `!present?`.

!user.email.present?
^^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `blank?` instead of `!present?`.

x.nil? || x.empty?
^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `x.blank?` instead of `x.nil? || x.empty?`.

name.nil? || name.empty?
^^^^^^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `name.blank?` instead of `name.nil? || name.empty?`.

foo == nil || foo.empty?
^^^^^^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `foo.blank?` instead of `foo == nil || foo.empty?`.

something unless foo.present?
          ^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `if foo.blank?` instead of `unless foo.present?`.

something unless present?
          ^^^^^^^^^^^^^^^ Rails/Blank: Use `if blank?` instead of `unless present?`.

unless foo.present?
^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `if foo.blank?` instead of `unless foo.present?`.
  something
end

!foo || foo.empty?
^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `foo.blank?` instead of `!foo || foo.empty?`.

!methods || methods.empty?
^^^^^^^^^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `methods.blank?` instead of `!methods || methods.empty?`.

!url || url.empty?
^^^^^^^^^^^^^^^^^^ Rails/Blank: Use `url.blank?` instead of `!url || url.empty?`.

return self if nil? || empty?
               ^^^^^^^^^^^^^^ Rails/Blank: Use `blank?` instead of `nil? || empty?`.
return [] if nil? || empty?
             ^^^^^^^^^^^^^^ Rails/Blank: Use `blank?` instead of `nil? || empty?`.

Current Fixture: no_offense.rb

tests/fixtures/cops/rails/blank/no_offense.rb

x.blank?
x.present?
!x.empty?
x.nil?
name.present? && name.length > 0
x.nil? || y.empty?
x.nil? && x.empty?
x.nil? || x.zero?
something if foo.present?
something unless foo.blank?
def blank?
  !present?
end
unless foo.present?
  something
else
  something_else
end

# present? called with argument (class method style) should NOT be flagged
# RuboCop's NodePattern `(send (send $_ :present?) :!)` requires present? with no arguments
!Helpers.present?(value)
!Vagrant::Util::Presence.present?(directory)
unless Helpers.present?(value)
  do_something
end

# safe navigation on present?/empty? — RuboCop's NodePattern matches send not csend
# so &.present? and &.empty? should NOT be flagged
return [] unless response&.strip&.present?
unless object&.present?
  do_something
end
foo.nil? || foo&.empty?

# pattern match guard: `in pattern unless condition` is not a regular unless
# RuboCop's on_if handler does not visit pattern match guards
case element.name
in "div" unless element.at("div").present?
  element.name = "p"
end

# safe navigation with !present? — semantics differ:
# !pkey_cols&.present? when pkey_cols is nil → !nil → true
# pkey_cols&.blank? when pkey_cols is nil → nil (falsy)
# RuboCop skips this pattern because `(send (send $_ :present?) :!)` doesn't
# match csend (safe navigation).
id_option = if pk_is_also_fk || !pkey_cols&.present?

Key Source Files

  • Rust implementation: src/cop/rails/blank.rs
  • RuboCop Ruby source (ground truth): vendor/rubocop-rails/lib/rubocop/cop/rails/blank.rb
  • RuboCop test excerpts: vendor/rubocop-rails/spec/rubocop/cop/rails/blank_spec.rb

Read these files before making changes.

Start Here

Use the existing corpus data to focus on the most concentrated regressions first.

Helpful local commands:

  • python3 scripts/investigate_cop.py Rails/Blank --repos-only
  • python3 scripts/investigate_cop.py Rails/Blank --context
  • python3 scripts/verify_cop_locations.py Rails/Blank

Top FN repos:

  • databasically__lowdown__d593927 (5 FN) — example vendor/rails/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb:594
  • cjstewart88__Tubalr__f6956c8 (4 FN) — example heroku/ruby/1.9.1/gems/rdoc-3.8/lib/rdoc/parser/c.rb:784
  • pitluga__supply_drop__d64c50c (4 FN) — example examples/vendored-puppet/vendor/puppet-2.7.8/lib/puppet/util/adsi.rb:161

Representative FN examples:

  • cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.8/lib/rdoc/parser/c.rb:784 — Use elements.blank? instead of elements.nil? or elements.empty?.
  • cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.8/lib/rdoc/ri/driver.rb:865 — Use name.blank? instead of name.nil? or name.empty?.
  • cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.9.4/lib/rdoc/parser/c.rb:782 — Use elements.blank? instead of elements.nil? or elements.empty?.

Pre-diagnostic Results

Diagnosis Summary

Each example was tested by running nitrocop on the extracted source in isolation
with --force-default-config to determine if the issue is a code bug or config issue.
Note: source context is truncated and may not parse perfectly. If a diagnosis
seems wrong (e.g., your test passes immediately for a 'CODE BUG'), treat it as
a config/context issue instead.

  • FN: 14 code bug(s), 0 config/context issue(s)

FN #1: cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.8/lib/rdoc/parser/c.rb:784

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Enclosing structure: if branch (line: if type.downcase == 'const' then)
The offense is inside this structure — the cop may need
to handle this context to detect the pattern.

Message: Use elements.blank?instead ofelements.nil? or elements.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      if elements.nil? or elements.empty? then
^ Rails/Blank: Use `elements.blank?` instead of `elements.nil? or elements.empty?`.

Full source context:

    # In the case of rb_define_const, the definition and comment are in
    # "/* definition: comment */" form.  The literal ':' and '\' characters
    # can be escaped with a backslash.
    if type.downcase == 'const' then
      elements = comment.split ':'

      if elements.nil? or elements.empty? then
        con = RDoc::Constant.new const_name, definition, comment
      else
        new_definition = elements[0..-2].join(':')

        if new_definition.empty? then # Default to literal C definition
          new_definition = definition
        else

FN #2: cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.8/lib/rdoc/ri/driver.rb:865

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Message: Use name.blank?instead ofname.nil? or name.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      return if name.nil? or name.empty?
^ Rails/Blank: Use `name.blank?` instead of `name.nil? or name.empty?`.

Full source context:

      name = if defined? Readline then
               Readline.readline ">> "
             else
               print ">> "
               $stdin.gets
             end

      return if name.nil? or name.empty?

      name = expand_name name.strip

      begin
        display_name name
      rescue NotFoundError => e
        puts e.message

FN #3: cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.9.4/lib/rdoc/parser/c.rb:782

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Enclosing structure: if branch (line: if type.downcase == 'const' then)
The offense is inside this structure — the cop may need
to handle this context to detect the pattern.

Message: Use elements.blank?instead ofelements.nil? or elements.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      if elements.nil? or elements.empty? then
^ Rails/Blank: Use `elements.blank?` instead of `elements.nil? or elements.empty?`.

Full source context:

    # In the case of rb_define_const, the definition and comment are in
    # "/* definition: comment */" form.  The literal ':' and '\' characters
    # can be escaped with a backslash.
    if type.downcase == 'const' then
      elements = comment.split ':'

      if elements.nil? or elements.empty? then
        con = RDoc::Constant.new const_name, definition, comment
      else
        new_definition = elements[0..-2].join(':')

        if new_definition.empty? then # Default to literal C definition
          new_definition = definition
        else

FN #4: cjstewart88__Tubalr__f6956c8: heroku/ruby/1.9.1/gems/rdoc-3.9.4/lib/rdoc/ri/driver.rb:878

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Message: Use name.blank?instead ofname.nil? or name.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      return if name.nil? or name.empty?
^ Rails/Blank: Use `name.blank?` instead of `name.nil? or name.empty?`.

Full source context:

      name = if defined? Readline then
               Readline.readline ">> "
             else
               print ">> "
               $stdin.gets
             end

      return if name.nil? or name.empty?

      name = expand_name name.strip

      begin
        display_name name
      rescue NotFoundError => e
        puts e.message

FN #5: databasically__lowdown__d593927: vendor/rails/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb:594

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Enclosing structure: method body (line: def __format(to_wrap) #:nodoc:)
The offense is inside this structure — the cop may need
to handle this context to detect the pattern.

Message: Use words[0].blank?instead ofwords[0].nil? or words[0].empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      words.shift if words[0].nil? or words[0].empty?
^ Rails/Blank: Use `words[0].blank?` instead of `words[0].nil? or words[0].empty?`.

Full source context:

  private
    def __do_split_word(word, size) #:nodoc:
      [word[0 .. (size - 1)], word[size .. -1]]
    end

    def __format(to_wrap) #:nodoc:
      words = to_wrap.split(/\s+/).compact
      words.shift if words[0].nil? or words[0].empty?
      to_wrap = []

      abbrev = false
      width = @columns - @first_indent - @left_margin - @right_margin
      indent_str = ' ' * @first_indent
      first_line = true
      line = words.shift

FN #6: databasically__lowdown__d593927: vendor/rails/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb:602

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Message: Use line.blank?instead ofline.nil? || line.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

      abbrev = __is_abbrev(line) unless line.nil? || line.empty?
^ Rails/Blank: Use `line.blank?` instead of `line.nil? || line.empty?`.

Full source context:

      to_wrap = []

      abbrev = false
      width = @columns - @first_indent - @left_margin - @right_margin
      indent_str = ' ' * @first_indent
      first_line = true
      line = words.shift
      abbrev = __is_abbrev(line) unless line.nil? || line.empty?

      while w = words.shift
        if (w.size + line.size < (width - 1)) ||
           ((line !~ LEQ_RE || abbrev) && (w.size + line.size < width))
          line << " " if (line =~ LEQ_RE) && (not abbrev)
          line << " #{w}"
        else

FN #7: databasically__lowdown__d593927: vendor/rails/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb:630

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Enclosing structure: block (do..end) (line: loop do)
The offense is inside this structure — the cop may need
to handle this context to detect the pattern.

Message: Use line.blank?instead ofline.nil? or line.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

        break if line.nil? or line.empty?
^ Rails/Blank: Use `line.blank?` instead of `line.nil? or line.empty?`.

Full source context:

          line = w
        end

        abbrev = __is_abbrev(w) unless w.nil?
      end

      loop do
        break if line.nil? or line.empty?
        line, w = __do_hyphenate(line, w, width) if @hard_margins
        to_wrap << __make_line(line, indent_str, width, w.nil?)
        line = w
      end

      if (@tag_paragraph && (to_wrap.size > 0)) then
        clr = %r{`(\w+)'}.match([caller(1)].flatten[0])[1]

FN #8: databasically__lowdown__d593927: vendor/rails/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb:790

NOT DETECTED — CODE BUG
The cop fails to detect this pattern. Fix the detection logic.

Enclosing structure: enclosing line: else
The offense is inside this structure — the cop may need
to handle this context to detect the pattern.

Message: Use rnext.blank?instead ofrnext.nil? or rnext.empty?.

Ready-made test snippet (add to offense.rb, adjust ^ count):

          break if rnext.nil? or rnext.empty? or rline.nil? or rline.empty?
^ Rails/Blank: Use `rnext.blank?` instead of `rnext.nil? or rnext.empty?`.

Full source context:

            words[-1] = first
            @split_words << SplitWord.new(word, first, rest)
          end
          rline = words.join(' ').strip
          rnext = "#{rest} #{rnext}".strip
          break
        else
          break if rnext.nil? or rnext.empty? or rline.nil? or rline.empty?
          words = rnext.split(/\s+/)
          word = words.shift
          size = width - rline.size - 1

          if (size <= 0)
            rnext = "#{word} #{words.join(' ')}".strip
            break

Omitted 6 additional diagnosed FN example(s) for brevity.

@6
Copy link
Copy Markdown
Contributor Author

6 bot commented Mar 26, 2026

Agent failed. See run: https://github.com/6/nitrocop/actions/runs/23572694990

@6 6 bot closed this Mar 26, 2026
@6 6 bot deleted the fix/rails-blank-23572694990 branch March 26, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants