Class: Rigor::Plugin::NodeRuleWalk

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/node_rule_walk.rb

Overview

ADR-52 WD4 — one engine-owned AST walk per file for node rules.

Before this, every plugin that declared a Base.node_rule walked the file’s AST itself (‘Base#node_rule_diagnostics` →`Source::NodeWalker.each_with_ancestors`), so a project with N node-rule plugins paid N walks per file. This folds them into a single walk that dispatches each visited node to every matching `(plugin, rule)` pair.

Behaviour is preserved exactly so the diagnostics stay byte-identical (the WD6 gate):

  • Each plugin’s ‘node_file_context` block runs once per file, before any of its rules fire, `instance_exec`’d on that plugin —same as the per-plugin walk.

  • One frozen NodeContext is built per node, lazily, only when at least one rule matches it. Because it wraps only the ancestors it is safe to share across plugins for the same node.

  • Each rule block is ‘instance_exec`’d on its own plugin instance with the same five arguments ‘(node, scope, path, file_context, context)`.

  • A plugin whose context block or any rule block raises has its whole node-rule contribution isolated — the walk records the error against that plugin and continues, matching the runner’s per-plugin rescue around the old ‘#node_rule_diagnostics` call.

  • Diagnostics are bucketed per plugin and returned in the registry order the runner already iterates, so emission order is unchanged (plugin-major, not node-major) — order preservation is what keeps the gate byte-identical in this slice.

The result is an ordered Array of Result, one per node-rule plugin (registry order). ‘Result#error` is non-nil iff that plugin’s context or a rule block raised, in which case ‘#diagnostics` is empty; the runner turns the error into the same per-plugin `runtime-error` envelope it produced before.

Defined Under Namespace

Classes: Result

Instance Method Summary collapse

Constructor Details

#initialize(plugins) ⇒ NodeRuleWalk

Plugins that declare at least one ‘node_rule`, paired with their frozen rule list, in registry order. Built once per run and reused for every file.



52
53
54
55
56
57
58
# File 'lib/rigor/plugin/node_rule_walk.rb', line 52

def initialize(plugins)
  @entries = plugins.filter_map do |plugin|
    rules = plugin.class.node_rules
    rules.empty? ? nil : [plugin, rules]
  end.freeze
  freeze
end

Instance Method Details

#diagnostics_for_file(path:, scope:, root:) ⇒ Object

Walk ‘root` once, dispatching every node to each matching `(plugin, rule)`. Returns an Array of Result in plugin (registry) order. `root` nil yields one empty Result per plugin.



67
68
69
70
71
72
73
# File 'lib/rigor/plugin/node_rule_walk.rb', line 67

def diagnostics_for_file(path:, scope:, root:)
  return @entries.map { |plugin, _| Result.new(plugin, [], nil) } if root.nil?

  states = @entries.map { |plugin, rules| State.new(plugin, rules, scope, root) }
  walk(path, scope, root, states)
  states.map(&:result)
end

#empty?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/rigor/plugin/node_rule_walk.rb', line 60

def empty?
  @entries.empty?
end