Class: Audition::Static::GraphAudit
- Inherits:
-
Object
- Object
- Audition::Static::GraphAudit
- Defined in:
- lib/audition/static/graph_audit.rb
Overview
Whole-program semantic checks backed by the rubydex graph.
rubydex resolves state to its true owner, so an ivar written in
the class body, in def self.x, and inside class << self,
across several files, unifies into one declaration owned by the
singleton class. Per-file AST visitors cannot see that.
Constant Summary collapse
- CVAR_WHY =
"Class variables cannot be accessed from non-main Ractors " \ "at all; both reads and writes raise " \ "Ractor::IsolationError (\"can not access class variables " \ "from non-main Ractors\")."
- CVAR_FIX =
"Replace with a deeply frozen constant, per-instance " \ "state, or Ractor-local storage (Ractor.current[:key], " \ "Ractor.store_if_absent)."
- STATE_WHY =
"This instance variable lives on the class/module object, " \ "which is shared across Ractors; non-main Ractors raise " \ "Ractor::IsolationError when writing it, and when reading " \ "it while it holds a non-shareable value (verified on " \ "Ruby 4.0)."
- STATE_FIX =
"Precompute and freeze the value at load time (for " \ "per-subclass values, in the inherited hook). For " \ "collections, rebuild and refreeze on write, " \ "Rails-style copy-on-write: self.list = " \ "(list + [item]).freeze; never mutate in place. As a " \ "last resort use Ractor.store_if_absent for lazy " \ "initialization or per-Ractor state in " \ "Ractor.current[:key]."
- FROZEN_MEMO_WHY =
"Every write memoizes a shareable (frozen) value, so " \ "non-main Ractors can read it once it has been " \ "computed; only the first write must happen on the " \ "main Ractor, or it raises Ractor::IsolationError. " \ "This is the pattern Rails core uses for its own " \ "memoized class state."
- FROZEN_MEMO_FIX =
"Warm the cache at boot, before spawning Ractors: call " \ "the memoizing method from an initializer or on_load " \ "hook. If the value genuinely must be computed at " \ "runtime, proxy the write to the main Ractor or use " \ "Ractor.store_if_absent."
Instance Method Summary collapse
-
#analyze_paths(paths) ⇒ Array<Finding>
rubydex's index_all descends directories but skips bare file lists, so files are fed through index_source individually.
- #analyze_sources(sources) ⇒ Array<Finding>
Instance Method Details
#analyze_paths(paths) ⇒ Array<Finding>
rubydex's index_all descends directories but skips bare file lists, so files are fed through index_source individually.
69 70 71 72 73 74 75 76 77 |
# File 'lib/audition/static/graph_audit.rb', line 69 def analyze_paths(paths) sources = {} paths.each do |path| sources[path] = File.read(path) rescue SystemCallError next end analyze_sources(sources) end |
#analyze_sources(sources) ⇒ Array<Finding>
54 55 56 57 58 59 60 61 62 |
# File 'lib/audition/static/graph_audit.rb', line 54 def analyze_sources(sources) graph = Rubydex::Graph.new sources.each do |path, code| graph.index_source(path, code, "ruby") end @sources = sources @frozen_memos = frozen_memo_map(sources) audit(graph) end |