Class: Shirobai::Cop::RSpec::MultipleMemoizedHelpers
- Inherits:
-
RuboCop::Cop::Base
- Object
- RuboCop::Cop::Base
- Shirobai::Cop::RSpec::MultipleMemoizedHelpers
- Includes:
- BundleEligible
- Defined in:
- lib/shirobai/cop/rspec/multiple_memoized_helpers.rb
Overview
Drop-in Rust reimplementation of RSpec/MultipleMemoizedHelpers
(rubocop-rspec 3.10.2).
Rust does the scope-tree work on the shared walk: for every plain-block
spec group it unions the memoized helpers visible from the group's own
frame and every parser-block ancestor frame (stock's all_helpers),
dedups them by node identity, and maps each let (plus subject when
AllowSubject: false) to its variable_definition? name. Names that are
decidable bytewise (sym value / str value / nil) are counted as
rust_distinct; dsym/dstr names cannot be (two interpolations that
differ only in whitespace are structurally EQUAL but not byte-equal), so
their source ranges are handed here. Rust emits a group only when the
safe upper bound rust_distinct + dsym_dstr_count > Max.
The wrapper locates the dsym/dstr nodes in the parser AST, dedups
them with stock's structural node equality (Array#uniq), computes
count = rust_distinct + located_uniq, and on a real count > Max
reports the group and records self.max = count through the same
exclude_limit 'Max' DSL stock uses (drives --auto-gen-config).
Constant Summary collapse
- MSG =
"Example group has too many memoized helpers [%<count>d/%<max>d]"
Class Method Summary collapse
- .badge ⇒ Object
-
.bundle_args(config) ⇒ Object
Packed args for the bundled run:
[Max, AllowSubject]. - .cop_name ⇒ Object
Instance Method Summary collapse
Class Method Details
.badge ⇒ Object
33 |
# File 'lib/shirobai/cop/rspec/multiple_memoized_helpers.rb', line 33 def self.badge = RuboCop::Cop::Badge.parse(cop_name) |
.bundle_args(config) ⇒ Object
Packed args for the bundled run: [Max, AllowSubject].
36 37 38 39 |
# File 'lib/shirobai/cop/rspec/multiple_memoized_helpers.rb', line 36 def self.bundle_args(config) cop_config = config.for_badge(badge) [cop_config.fetch("Max", 5), cop_config.fetch("AllowSubject", true) ? 1 : 0] end |
.cop_name ⇒ Object
32 |
# File 'lib/shirobai/cop/rspec/multiple_memoized_helpers.rb', line 32 def self.cop_name = "RSpec/MultipleMemoizedHelpers" |
Instance Method Details
#on_new_investigation ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/shirobai/cop/rspec/multiple_memoized_helpers.rb', line 41 def on_new_investigation groups = resolved_groups return if groups.empty? max = cop_config["Max"] buffer = processed_source.buffer source = bundle_eligible? ? processed_source.raw_source : buffer.source off = SourceOffsets.for(source) located = locate_dynamic_names(groups, off) groups.each do |(gstart, gend, rust_distinct, dyn_ranges)| # A locate miss (a prism/parser range disagreement we have not # met yet) falls back to the range itself as the identity, so it # degrades to per-occurrence distinctness instead of silently # merging every missed node into one `nil`. nodes = dyn_ranges.map { |(s, e)| located[[off[s], off[e]]] || [off[s], off[e]] } count = rust_distinct + nodes.uniq.length next if count <= max self.max = count range = Parser::Source::Range.new(buffer, off[gstart], off[gend]) add_offense(range, message: format(MSG, count: count, max: max)) end end |