B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
2.6 KiB
| id | repo | severity | status | created |
|---|---|---|---|---|
| rubocop-0001 | RuboCop | MEDIUM | OPEN | 2026-03-26 |
Defect
File: lib/rubocop/cop/ignored_node.rb:32
Pattern: @ignored_nodes ||= [] — Array used as the ignored-node set; linear scan on every membership test
Complexity: O(R × S) per file, where R = regexp nodes and S = string nodes
Language: Ruby
Description
IgnoredNode is a module included in Cop::Base, which every cop inherits. Two methods use the
array:
ignored_node?(node)(line 26):ignored_nodes.any? { |n| n.equal?(node) }— O(n) linear scanpart_of_ignored_node?(node)(line 12):ignored_nodes.map(&:loc).any? { ... }— O(n) with additional.mapallocation per call
The most impactful call path is via the StringHelp mixin (included by Style::StringLiterals,
Style::StringLiteralsInInterpolation, Style::CharacterLiteral, Style::IpAddresses):
on_regexp(node) → ignore_node(node) → @ignored_nodes << node # grow
on_str(node) → part_of_ignored_node?(node) # scan all ignored nodes
For a file containing R regexp literals and S string literals, part_of_ignored_node? is called S
times, each scanning up to R entries: O(R × S) total work within that file. On a file with
100 regexps and 1 000 strings that is 100 000 linear comparisons. Across a large project of
10 000 files the cumulative cost is substantial.
Fix
Replace the backing array with a Set using identity comparison:
# ignored_node.rb
def ignored_nodes
@ignored_nodes ||= Set.new.compare_by_identity
end
def ignore_node(node)
ignored_nodes.add(node)
end
def ignored_node?(node)
ignored_nodes.include?(node) # O(1)
end
def part_of_ignored_node?(node)
ignored_nodes.any? do |ignored|
loc = ignored.loc
# ... same range check as today
end # O(n) worst-case but Set iteration is cache-friendly
end
For part_of_ignored_node? the range comparison cannot be reduced to a hash lookup, but switching
to a Set eliminates the .map allocation and makes ignored_node? O(1).
Speedup estimate
Proportional to R × S per file; practically 2–10× faster on files with many regexp and string literals. Negligible on files with few regexps.
Work required
- Patch in
defects/rubocop/patch/ - Unit test — asserts operation counts before/after (in
defects/rubocop/unit/) - Integration test (in
defects/rubocop/integration/) - Benchmark — before/after on files with varying R and S (in
defects/rubocop/bench/) - White paper section