Skip to content

[bot] Fix Style/Attr#876

Open
6[bot] wants to merge 2 commits intomainfrom
fix/style-attr-23729855269
Open

[bot] Fix Style/Attr#876
6[bot] wants to merge 2 commits intomainfrom
fix/style-attr-23729855269

Conversation

@6
Copy link
Copy Markdown
Contributor

@6 6 bot commented Mar 30, 2026

Automated fix fix for Style/Attr corpus conformance.

Refs #323

Details

Cop Style/Attr
Backend codex / hard
Model gpt-5.4 (xhigh)
Mode fix
Turns 1
Run #434

Result

Updated [src/cop/style/attr.rs](/home/runner/work/nitrocop/nitrocop/src/cop/style/attr.rs) to stop skipping receiverless parenthesized `attr(...)` calls and to match RuboCop’s replacement choice more closely by using the last boolean argument for `attr_accessor` vs `attr_reader`. I also updated the Style/Attr fixtures in [offense.rb](/home/runner/work/nitrocop/nitrocop/tests/fixtures/cops/style/attr/offense.rb), [no_offense.rb](/home/runner/work/nitrocop/nitrocop/tests/fixtures/cops/style/attr/no_offense.rb), and [corrected.rb](/home/runner/work/nitrocop/nitrocop/tests/fixtures/cops/style/attr/corrected.rb) to cover the missed parenthesized and nested-call cases and to remove the stale `attr(:name)` no-offense expectation.

Verification passed: `cargo test --lib -- cop::style::attr` and `python3 scripts/check_cop.py Style/Attr --rerun --clone --sample 15`. The corpus gate reported `New FP: 0`, `New FN: 0`, and `Resolved FN: 25`. Commit: `7bd5f6a7` (`Fix Style/Attr parenthesized attr handling`).

Changes

  4 files changed
Agent conversation excerpt

Reading the CI instructions and the existing Style/Attr implementation first, then I’ll add a failing fixture that matches the corpus FN pattern before touching the cop logic.

Reading the CI instructions and the existing Style/Attr implementation first, then I’ll add a failing fixture that matches the corpus FN pattern before touching the cop logic.

exec_command: sed -n '1,220p' docs/agent-ci.md

exec_command: sed -n '1,220p' src/cop/style/attr.rs

exec_command: sed -n '1,220p' vendor/rubocop/lib/rubocop/cop/style/attr.rb

exec_command: sed -n '1,240p' vendor/rubocop/spec/rubocop/cop/style/attr_spec.rb

The current cop has an explicit opening_loc guard that skips all parenthesized attr(...) calls. I’m checking RuboCop on those patterns now because the existing local fixture says attr(:name) is allowed, which looks inconsistent with the corpus FN examples.

The current cop has an explicit opening_loc guard that skips all parenthesized attr(...) calls. I’m checking RuboCop on those patterns now because the existing local fixture says attr(:name) is allowed, which looks inconsistent with the corpus FN examples.

exec_command: printf 'class Parenthesized\n attr(:name)\nend\n' > /tmp/test.rb && rubocop --only Style/Attr /tmp/test.rb

exec_command: printf 'module FortyFacets\n class OrderDefinition\n attr(:title, :clause, :default)\n end\nend\n' > /tmp/test2.rb && rubocop --only Style/Attr /tmp/test2.rb

exec_command: printf 'class SomeClass\n def attr(*args)\n p args\n end\n\n def a\n attr(1)\n end\nend\n' > /tmp/test3.rb && rubocop --only Style/Attr /tmp/test3.rb

exec_command: sed -n '1,220p' tests/fixtures/cops/style/attr/offense.rb sed -n '1,220p' tests/fixtures/cops/style/attr/no_offense.rb

write_stdin: ``

write_stdin: ``

write_stdin: ``

exec_command: git status --short

The workspace already has an uncommitted edit in the Style/Attr offense fixture. I’m inspecting that diff before I touch anything so I can avoid trampling a prior attempt and make the next change set consistent.

The workspace already has an uncommitted edit in the Style/Attr offense fixture. I’m inspecting that diff before I touch anything so I can avoid trampling a prior attempt and make the next change set consistent.

exec_command: git diff -- tests/fixtures/cops/style/attr/offense.rb

exec_command: cargo test --lib -- cop::style::attr

exec_command: python3 scripts/verify_cop_locations.py Style/Attr

write_stdin: ``

write_stdin: ``

The pre-added fixture cases are real: RuboCop flags attr(:name) in a class body, while it still accepts attr(1) when a local def attr exists. I’m letting the targeted test run finish next so I can confirm the current failure shape before editing the cop.

The pre-added fixture cases are real: RuboCop flags attr(:name) in a class body, while it still accepts attr(1) when a local def attr exists. I’m letting the targeted test run finish next so I can confirm the current failure shape before editing the cop.

write_stdin: ``

write_stdin: ``

write_stdin: ``

I have the targeted failure now: the existing cop still only catches the original 4 fixtures and misses every newly added parenthesized/dynamic-call case. Next I’m drilling into Prism node shapes for those missed examples so the fix can stay narrow and preserve the 686 existing matches.

I have the targeted failure now: the existing cop still only catches the original 4 fixtures and misses every newly added parenthesized/dynamic-call case. Next I’m drilling into Prism node shapes for those missed examples so the fix can stay narrow and preserve the 686 existing matches.

exec_command: rg -n "as_call_node\(|opening_loc\(|message_loc\(|as_def_node\(|scope_defines_attr_method" -n src/cop -g '*.rs'

exec_command: sed -n '220,420p' src/cop/style/attr.rs

exec_command: rg -n "ParseResult|Prism|debug.*node|dump.*ast|opening_loc" -g '*.rs' src tests

The likely bug is narrower than the current guard suggests: RuboCop treats receiverless attr(...) as an offense even with parentheses, so the blanket opening_loc() early return is wrong. I’m confirming the AST shape and then I’ll patch the cop plus the stale no_offense fixture that still expects attr(:name) to be allowed.

The likely bug is narrower than the current guard suggests: RuboCop treats receiverless attr(...) as an offense even with parentheses, so the blanket opening_loc() early return is wrong. I’m confirming the AST shape and then I’ll patch the cop plus the stale no_offense fixture that still expects attr(:name) to be allowed.

exec_command: ruby -r prism -e 'src=%q{class Parenthesized\n attr(:name)\nend\n}; pp Prism.parse(src).value'

exec_command: ruby -r prism -e 'src=%q{class SomeClass\n def attr(*args)\n args\n end\n\n def call\n attr(1)\n end\nend\n}; pp Prism.parse(src).value'

exec_command: printf 'class SomeClass\n def attr(*args)\n end\n\n attr(:name)\nend\n' > /tmp/test4.rb && rubocop --only Style/Attr /tmp/test4.rb

write_stdin: ``

exec_command: ls -1 tests/fixtures/cops/style/attr

exec_command: sed -n '1,220p' tests/fixtures/cops/style/attr/autocorrect.rb

exec_command: sed -n '1,220p' tests/fixtures/cops/style/attr/corrected.rb

Plan after the initial read-through:

  1. completed Read docs/agent-ci.md, the current Style/Attr cop, RuboCop’s source/spec, and the existing fixture state.
  2. in_progress Patch the stale fixture set so it matches RuboCop: keep the FN cases in offense.rb, update corrected.rb, and replace the invalid attr(:name) no-offense with a real allowed parenthesized custom-attr case.
  3. pending Remove the broad parenthesized-call skip in src/cop/style/attr.rs, add a /// doc comment describing the FN root cause, and keep the custom-def attr scope guard intact.
  4. pending Run cargo test --lib -- cop::style::attr, then python3 scripts/check_cop.py Style/Attr --rerun --clone --sample 15, and only then commit the cop files.

Plan after the initial read-through:

  1. completed Read docs/agent-ci.md, the current Style/Attr cop, RuboCop’s source/spec, and the existing fixture state.
  2. in_progress Patch the stale fixture set so it matches RuboCop: keep the FN cases in offense.rb, update corrected.rb, and replace the invalid attr(:name) no-offense with a real allowed parenthesized custom-attr case.
  3. pending Remove the broad parenthesized-call skip in src/cop/style/attr.rs, add a /// doc comment describing the FN root cause, and keep the custom-def attr scope guard intact.
  4. pending Run cargo test --lib -- cop::style::attr, then python3 scripts/check_cop.py Style/Attr --rerun --clone --sample 15, and only then commit the cop files.

update_plan

I’m about to edit the fixture set first. Before that, I’m checking RuboCop’s actual autocorrections for the parenthesized reader/accessor cases so corrected.rb matches ground truth instead of just matching whatever the current Rust code happens to emit.

I’m about to edit the fixture set first. Before that, I’m checking RuboCop’s actual autocorrections for the parenthesized reader/accessor cases so corrected.rb matches ground truth instead of just matching whatever the current Rust code happens to emit.

exec_command: printf 'attr(:name)\n' > /tmp/attr_reader_case.rb && rubocop -A --only Style/Attr /tmp/attr_reader_case.rb && cat /tmp/attr_reader_case.rb

exec_command: printf "File.expand_path(attr('docdir', \"\", true))\n" > /tmp/attr_accessor_case.rb && rubocop -A --only Style/Attr /tmp/attr_accessor_case.rb && cat /tmp/attr_accessor_case.rb

exec_command: printf 'class SomeClass\n def attr(*args)\n args\n end\n\n def call\n attr(:name)\n end\nend\n' > /tmp/custom_parenthesized.rb && rubocop --only Style/Attr /tmp/custom_parenthesized.rb

write_stdin: ``

write_stdin: ``

There’s a second issue beyond parentheses: the current Rust cop decides attr_accessor vs attr_reader from the second argument, but RuboCop uses the last argument for the message and only uses the second argument when deciding whether it can safely delete the boolean tail during autocorrect. I’m verifying the multi-call/column details before patching the fixtures so the test file matches RuboCop exactly.

There’s a second issue beyond parentheses: the current Rust cop decides attr_accessor vs attr_reader from the second argument, but RuboCop uses the last argument for the message and only uses the second argument when deciding whether it can safely delete the boolean tail during autocorrect. I’m verifying the multi-call/column details before patching the fixtures so the test file matches RuboCop exactly.

exec_command: printf "attr(name) || attr(name, default_value, 'diagram')\n" > /tmp/two_attr_calls.rb && rubocop --only Style/Attr /tmp/two_attr_calls.rb

Task prompt (5690 tokens)

Fix Style/Attr — 0 FP, 25 FN

Instructions

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

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

⚠ 686 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 Style/Attr /tmp/test.rb
    echo '<general pattern>' > /tmp/test.rb && rubocop --only Style/Attr /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/style/attr/offense.rb with ^ annotation
    • FP fix: add the false-positive pattern to tests/fixtures/cops/style/attr/no_offense.rb
  4. Verify test fails: cargo test --lib -- cop::style::attr
  5. Fix src/cop/style/attr.rs
  6. Verify test passes: cargo test --lib -- cop::style::attr
  7. Validate against corpus (REQUIRED before committing):
    python3 scripts/check_cop.py Style/Attr --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 Style/Attr: <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 Style/Attr /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/style/attr.rs and tests/fixtures/cops/style/attr/
  • Run cargo test --lib -- cop::style::attr to verify your fix (do NOT run the full test suite)
  • Run python3 scripts/check_cop.py Style/Attr --rerun --clone --sample 15 before committing to catch regressions
  • Do NOT touch unrelated files
  • Do NOT use git stash
  • Do NOT push — you do not have push permission; the workflow handles pushing after you exit

Current Fixture: offense.rb

tests/fixtures/cops/style/attr/offense.rb

class Foo
  attr :writable, true
  ^^^^ Style/Attr: Do not use `attr`. Use `attr_accessor` instead.
end

class Bar
  attr :one, :two, :three
  ^^^^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.
end

class Baz
  attr :name
  ^^^^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.
end

class Qux
  attr :readable, false
  ^^^^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.
end

Current Fixture: no_offense.rb

tests/fixtures/cops/style/attr/no_offense.rb

class Foo
  attr_reader :something

  attr_accessor :name

  attr_writer :value
end

class SomeClass
  def attr(*args)
    args
  end

  def call
    attr(1)
  end
end

class Parenthesized
  attr(:name)
end

Key Source Files

  • Rust implementation: src/cop/style/attr.rs
  • RuboCop Ruby source (ground truth): vendor/rubocop/lib/rubocop/cop/style/attr.rb
  • RuboCop test excerpts: vendor/rubocop/spec/rubocop/cop/style/attr_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 Style/Attr --repos-only
  • python3 scripts/investigate_cop.py Style/Attr --context
  • python3 scripts/verify_cop_locations.py Style/Attr

Top FN repos:

  • aws__aws-record-ruby__7ef7a2e (13 FN) — example lib/aws-record/record/attributes.rb:158
  • asciidoctor__asciidoctor-diagram__11462a8 (3 FN) — example lib/asciidoctor-diagram/diagram_source.rb:34
  • tombenner__nikkou__3159aff (3 FN) — example lib/nikkou/nokogiri/xml/node.rb:80

Representative FN examples:

  • FortySource__forty_facets__98a4f77: lib/forty_facets/filter_definition.rb:5 — Do not use attr. Use attr_reader instead.
  • FortySource__forty_facets__98a4f77: lib/forty_facets/order_definition.rb:4 — Do not use attr. Use attr_reader instead.
  • SketchUp__sketchup-stl__0b83322: src/sketchup-stl/SKUI/control_manager.rb:7 — Do not use attr. Use attr_reader instead.

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: 15 code bug(s)

FN #1: FortySource__forty_facets__98a4f77: lib/forty_facets/filter_definition.rb:5

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

Full-file enclosing chain: class body (line 3: class FilterDefinition) > module body (line 1: module FortyFacets)

Message: Do not use attr. Use attr_reader instead.

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

    attr(:search, :path, :options, :joins, :table_name, :column_name,
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

        1: module FortyFacets
        2:   # Base class for the classes storing the definition of differently behaving filters
        3:   class FilterDefinition
        4: 
>>>     5:     attr(:search, :path, :options, :joins, :table_name, :column_name,
        6:       :origin_class, :association, :attribute)
        7: 
        8:     def initialize search, path, options
        9:       @search = search
       10:       @path = [path].flatten
       11:       @options = options
       12: 

FN #2: FortySource__forty_facets__98a4f77: lib/forty_facets/order_definition.rb:4

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

Full-file enclosing chain: class body (line 3: class OrderDefinition) > module body (line 1: module FortyFacets)

Message: Do not use attr. Use attr_reader instead.

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

    attr(:title, :clause, :default)
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

        1: module FortyFacets
        2:   # Stores the parameters of a order criteria for a search.
        3:   class OrderDefinition
>>>     4:     attr(:title, :clause, :default)
        5: 
        6:     def initialize title, clause
        7:       @title = title
        8:       @clause = clause
        9:       @default = false
       10: 
       11:       if clause.is_a? Hash

FN #3: SketchUp__sketchup-stl__0b83322: src/sketchup-stl/SKUI/control_manager.rb:7

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

Full-file enclosing chain: module body (line 4: module ControlManager) > module body (line 1: module SKUI)

Message: Do not use attr. Use attr_reader instead.

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

    attr( :controls )
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

        1: module SKUI
        2:   # @abstract `Container` and `Window` implements this.
        3:   # @since 1.0.0
        4:   module ControlManager
        5: 
        6:     # @since 1.0.0
>>>     7:     attr( :controls )
        8: 
        9:     # @since 1.0.0
       10:     def initialize
       11:       super()
       12:       @controls = []
       13:     end
       14: 

FN #4: asciidoctor__asciidoctor-diagram__11462a8: lib/asciidoctor-diagram/diagram_source.rb:34

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

Full-file enclosing chain: method body (line 33: def global_attr(name, default_value = nil)) > module body (line 8: module DiagramSource) > module body (line 5: module Diagram) > module body (line 4: module Asciidoctor)

Message: Do not use attr. Use attr_reader instead.

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

        attr(name) || attr(name, default_value, 'diagram')
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

        4: module Asciidoctor
        5:   module Diagram
        6:     # This module describes the duck-typed interface that diagram sources must implement. Implementations
        7:     # may include this module but it is not required.
        8:     module DiagramSource
        9:       include Asciidoctor::Logging
       10: 
       11:       def diagram_type
       12:         raise NotImplementedError.new
       13:       end
       14: 
       15:       def image_name
       16:         raise NotImplementedError.new
       17:       end
       18: 
       19:       # @return [String] the String representation of the source code for the diagram
       20:       # @abstract
       21:       def code
       22:         @code ||= load_code
       23:       end
       24: 
       25:       def load_code
       26:         raise NotImplementedError.new
       27:       end
       28: 
       29:       def global_opt(opt)
       30:         global_attr("#{opt}-option")
       31:       end
       32: 
       33:       def global_attr(name, default_value = nil)
>>>    34:         attr(name) || attr(name, default_value, 'diagram')
       35:       end
       36: 
       37:       def opt(opt)
       38:         attr("#{opt}-option")
       39:       end
       40: 
       41:       # Get the value for the specified attribute. First look in the attributes on

FN #5: asciidoctor__asciidoctor-diagram__11462a8: lib/asciidoctor-diagram/diagram_source.rb:38

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

Full-file enclosing chain: method body (line 37: def opt(opt)) > module body (line 8: module DiagramSource) > module body (line 5: module Diagram) > module body (line 4: module Asciidoctor)

Message: Do not use attr. Use attr_reader instead.

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

        attr("#{opt}-option")
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

        8:     module DiagramSource
        9:       include Asciidoctor::Logging
       10: 
       11:       def diagram_type
       12:         raise NotImplementedError.new
       13:       end
       14: 
       15:       def image_name
       16:         raise NotImplementedError.new
       17:       end
       18: 
       19:       # @return [String] the String representation of the source code for the diagram
       20:       # @abstract
       21:       def code
       22:         @code ||= load_code
       23:       end
       24: 
       25:       def load_code
       26:         raise NotImplementedError.new
       27:       end
       28: 
       29:       def global_opt(opt)
       30:         global_attr("#{opt}-option")
       31:       end
       32: 
       33:       def global_attr(name, default_value = nil)
       34:         attr(name) || attr(name, default_value, 'diagram')
       35:       end
       36: 
       37:       def opt(opt)
>>>    38:         attr("#{opt}-option")
       39:       end
       40: 
       41:       # Get the value for the specified attribute. First look in the attributes on
       42:       # this document and return the value of the attribute if found. Otherwise, if
       43:       # this document is a child of the Document document, look in the attributes of the
       44:       # Document document and return the value of the attribute if found. Otherwise,
       45:       # return the default value, which defaults to nil.

FN #6: asciidoctor__asciidoctor-diagram__11462a8: lib/asciidoctor-diagram/diagram_source.rb:61

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

Full-file enclosing chain: method body (line 60: def base_dir) > module body (line 8: module DiagramSource) > module body (line 5: module Diagram) > module body (line 4: module Asciidoctor)

Message: Do not use attr. Use attr_accessor instead.

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

        File.expand_path(attr('docdir', "", true))
^ Style/Attr: Do not use `attr`. Use `attr_accessor` instead.

Full file context (30 lines before offense):

       31:       end
       32: 
       33:       def global_attr(name, default_value = nil)
       34:         attr(name) || attr(name, default_value, 'diagram')
       35:       end
       36: 
       37:       def opt(opt)
       38:         attr("#{opt}-option")
       39:       end
       40: 
       41:       # Get the value for the specified attribute. First look in the attributes on
       42:       # this document and return the value of the attribute if found. Otherwise, if
       43:       # this document is a child of the Document document, look in the attributes of the
       44:       # Document document and return the value of the attribute if found. Otherwise,
       45:       # return the default value, which defaults to nil.
       46:       #
       47:       # @param name [String, Symbol, Array] the name(s) of the attribute to lookup
       48:       # @param default_value [Object] the value to return if the attribute is not found
       49:       # @inherit [Boolean, String] indicates whether to check for the attribute on the AsciiDoctor::Document if not found on this document.
       50:       #                            When a non-nil String is given the an attribute name "#{inherit}-#{name}" is looked for on the document.
       51:       #
       52:       # @return the value of the attribute or the default value if the attribute is not found in the attributes of this node or the document node
       53:       # @abstract
       54:       def attr(name, default_value = nil, inherit = diagram_type)
       55:         raise NotImplementedError.new
       56:       end
       57: 
       58:       # @return [String] the base directory against which relative paths in this diagram should be resolved
       59:       # @abstract
       60:       def base_dir
>>>    61:         File.expand_path(attr('docdir', "", true))
       62:       end
       63: 
       64:       # Alias for code
       65:       def to_s
       66:         code
       67:       end
       68: 

FN #7: aws__aws-record-ruby__7ef7a2e: lib/aws-record/record/attributes.rb:158

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

Full-file enclosing chain: method body (line 156: def string_attr(name, opts = {})) > module body (line 102: module ClassMethods) > module body (line 5: module Attributes) > module body (line 4: module Record) > module body (line 3: module Aws)

Message: Do not use attr. Use attr_reader instead.

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

          attr(name, Marshalers::StringMarshaler.new(opts), opts)
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

      128:         #   can be used as a default value.
      129:         # @option opts [Boolean] :hash_key Set to true if this attribute is
      130:         #   the hash key for the table.
      131:         # @option opts [Boolean] :range_key Set to true if this attribute is
      132:         #   the range key for the table.
      133:         def attr(name, marshaler, opts = {})
      134:           @attributes.register_attribute(name, marshaler, opts)
      135:           _define_attr_methods(name)
      136:           _key_attributes(name, opts)
      137:         end
      138: 
      139:         # Define a string-type attribute for your model.
      140:         #
      141:         # @param [Symbol] name Name of this attribute.  It should be a name that
      142:         #   is safe to use as a method.
      143:         # @param [Hash] opts
      144:         # @option opts [Boolean] :hash_key Set to true if this attribute is
      145:         #   the hash key for the table.
      146:         # @option opts [Boolean] :range_key Set to true if this attribute is
      147:         #   the range key for the table.
      148:         # @option opts [Boolean] :persist_nil Optional attribute used to
      149:         #   indicate whether nil values should be persisted. If true, explicitly
      150:         #   set nil values will be saved to DynamoDB as a "null" type. If false,
      151:         #   nil values will be ignored and not persisted. By default, is false.
      152:         # @option opts [Object] :default_value Optional attribute used to
      153:         #   define a "default value" to be used if the attribute's value on an
      154:         #   item is nil or not set at persistence time. Additionally, lambda
      155:         #   can be used as a default value.
      156:         def string_attr(name, opts = {})
      157:           opts[:dynamodb_type] = 'S'
>>>   158:           attr(name, Marshalers::StringMarshaler.new(opts), opts)
      159:         end
      160: 
      161:         # Define a boolean-type attribute for your model.
      162:         #
      163:         # @param [Symbol] name Name of this attribute.  It should be a name that
      164:         #   is safe to use as a method.
      165:         # @param [Hash] opts

FN #8: aws__aws-record-ruby__7ef7a2e: lib/aws-record/record/attributes.rb:180

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

Full-file enclosing chain: method body (line 178: def boolean_attr(name, opts = {})) > module body (line 102: module ClassMethods) > module body (line 5: module Attributes) > module body (line 4: module Record) > module body (line 3: module Aws)

Message: Do not use attr. Use attr_reader instead.

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

          attr(name, Marshalers::BooleanMarshaler.new(opts), opts)
^ Style/Attr: Do not use `attr`. Use `attr_reader` instead.

Full file context (30 lines before offense):

      150:         #   set nil values will be saved to DynamoDB as a "null" type. If false,
      151:         #   nil values will be ignored and not persisted. By default, is false.
      152:         # @option opts [Object] :default_value Optional attribute used to
      153:         #   define a "default value" to be used if the attribute's value on an
      154:         #   item is nil or not set at persistence time. Additionally, lambda
      155:         #   can be used as a default value.
      156:         def string_attr(name, opts = {})
      157:           opts[:dynamodb_type] = 'S'
      158:           attr(name, Marshalers::StringMarshaler.new(opts), opts)
      159:         end
      160: 
      161:         # Define a boolean-type attribute for your model.
      162:         #
      163:         # @param [Symbol] name Name of this attribute.  It should be a name that
      164:         #   is safe to use as a method.
      165:         # @param [Hash] opts
      166:         # @option opts [Boolean] :hash_key Set to true if this attribute is
      167:         #   the hash key for the table.
      168:         # @option opts [Boolean] :range_key Set to true if this attribute is
      169:         #   the range key for the table.
      170:         # @option opts [Boolean] :persist_nil Optional attribute used to
      171:         #   indicate whether nil values should be persisted. If true, explicitly
      172:         #   set nil values will be saved to DynamoDB as a "null" type. If false,
      173:         #   nil values will be ignored and not persisted. By default, is false.
      174:         # @option opts [Object] :default_value Optional attribute used to
      175:         #   define a "default value" to be used if the attribute's value on an
      176:         #   item is nil or not set at persistence time. Additionally, lambda
      177:         #   can be used as a default value.
      178:         def boolean_attr(name, opts = {})
      179:           opts[:dynamodb_type] = 'BOOL'
>>>   180:           attr(name, Marshalers::BooleanMarshaler.new(opts), opts)
      181:         end
      182: 
      183:         # Define a integer-type attribute for your model.
      184:         #
      185:         # @param [Symbol] name Name of this attribute.  It should be a name that
      186:         #   is safe to use as a method.
      187:         # @param [Hash] opts

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

@6 6 bot force-pushed the fix/style-attr-23729855269 branch from 7bd5f6a to c5ebb0a Compare March 30, 2026 06:05
@6 6 bot marked this pull request as ready for review March 30, 2026 06:05
@6 6 bot enabled auto-merge (squash) March 30, 2026 06:06
@github-actions
Copy link
Copy Markdown
Contributor

Cop-check results (8 shards)

Cop Baseline FP Baseline FN Local FP Local FN FP Δ FN Δ Result
Style/Attr 0 0 0 0 0 0
Style/Attr 0 0 0 0 0 0
Style/Attr 0 4 0 0 0 -4
Style/Attr 0 7 0 0 0 -7
Style/Attr 0 0 0 0 0 0
Style/Attr 0 0 0 0 0 0
Style/Attr 0 13 0 0 0 -13
Style/Attr 0 1 0 0 0 -1

All shards passed — ready to merge.

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