Class: Shirobai::Cop::Lint::AmbiguousBlockAssociation

Inherits:
RuboCop::Cop::Base
  • Object
show all
Extended by:
RuboCop::Cop::AutoCorrector
Defined in:
lib/shirobai/cop/lint/ambiguous_block_association.rb

Overview

Drop-in Rust reimplementation of ‘Lint/AmbiguousBlockAssociation`.

Detection and autocorrect both happen in Rust; Ruby turns the byte offsets handed back into offenses and a ‘remove + insert_before + insert_after` corrector trio (matching stock’s ‘wrap_in_parentheses` byte-for-byte).

‘AllowedMethods` is taken from the cop’s own config (verbatim string entries; ‘Regexp` entries are filtered out and force the standalone fallback). `AllowedPatterns` (regexp) is matched in Ruby: the wrapper walks every block-bearing call candidate’s INNER sender source and collects the strings that match any pattern, then hands the list to the standalone Rust entry as ‘allowed_inner_sources` so the Rust path can drop them by exact-bytes lookup. Without `AllowedPatterns` the bundle path is taken (no regexp work).

Constant Summary collapse

MSG =
"Parenthesize the param `%<param>s` to make sure that the " \
"block will be associated with the `%<method>s` method " \
"call."

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.badgeObject



29
# File 'lib/shirobai/cop/lint/ambiguous_block_association.rb', line 29

def self.badge = RuboCop::Cop::Badge.parse(cop_name)

.bundle_args(config) ⇒ Object

Returns ‘[allowed_methods]`. `allowed_methods` mirrors the stock `AllowedMethods` mixin: regexp entries filtered out (any regexp forces the wrapper into the standalone path via `bundle_eligible?`).



34
35
36
37
38
39
# File 'lib/shirobai/cop/lint/ambiguous_block_association.rb', line 34

def self.bundle_args(config)
  cfg = config.for_badge(badge)
  allowed = Array(cfg.fetch("AllowedMethods", []))
  names = allowed.reject { |e| e.is_a?(Regexp) }.map(&:to_s)
  [names]
end

.cop_nameObject



28
# File 'lib/shirobai/cop/lint/ambiguous_block_association.rb', line 28

def self.cop_name = "Lint/AmbiguousBlockAssociation"

Instance Method Details

#bundle_eligible?Boolean

Bundle path is OK only when ‘AllowedMethods` has no regexp AND `AllowedPatterns` is empty (regexp matching against inner-sender source is done in Ruby; the bundle path does not carry the pre-applied list).

Returns:

  • (Boolean)


45
46
47
48
49
50
# File 'lib/shirobai/cop/lint/ambiguous_block_association.rb', line 45

def bundle_eligible?
  cfg = cop_config
  allowed = Array(cfg.fetch("AllowedMethods", []))
  patterns = Array(cfg.fetch("AllowedPatterns", []))
  allowed.none? { |e| e.is_a?(Regexp) } && patterns.empty?
end

#on_new_investigationObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/shirobai/cop/lint/ambiguous_block_association.rb', line 52

def on_new_investigation
  buffer = processed_source.buffer
  off = SourceOffsets.for(processed_source.raw_source)
  fetch_offenses.each do |start_offset, end_offset, param_start, param_end,
                          inner_send_start, inner_send_end,
                          ac_open_start, ac_open_end, ac_close_pos|
    range = Parser::Source::Range.new(buffer, off[start_offset], off[end_offset])
    param_range = Parser::Source::Range.new(buffer, off[param_start], off[param_end])
    inner_range = Parser::Source::Range.new(buffer, off[inner_send_start], off[inner_send_end])
    message = format(MSG, param: param_range.source, method: inner_range.source)
    add_offense(range, message: message) do |corrector|
      open_range = Parser::Source::Range.new(buffer, off[ac_open_start], off[ac_open_end])
      close_range = Parser::Source::Range.new(buffer, off[ac_close_pos], off[ac_close_pos])
      corrector.remove(open_range)
      corrector.insert_before(open_range, "(")
      corrector.insert_after(close_range, ")")
    end
  end
end