Class: Audition::Static::Checks::Base
- Inherits:
-
Prism::Visitor
- Object
- Prism::Visitor
- Audition::Static::Checks::Base
- Defined in:
- lib/audition/static/checks/base.rb
Overview
A check is a Prism visitor plus a message catalog, written in a small declarative DSL:
class MyCheck < Base
check_name "my-check" # optional; derived
explain :bad_thing,
severity: :error,
message: "found %{name}",
why: "it breaks because ...",
fix: "do this instead ..."
on :call_node do |node|
flag(node, :bad_thing, name: node.name)
end
end
on generates the visit method and always continues into
child nodes, so handlers cannot accidentally stop traversal.
Third-party checks plug in via Checks.register(MyCheck).
Direct Known Subclasses
GlobalVariables, MutableConstants, RactorIsolation, RuntimeRequire, UnsafeCalls
Constant Summary collapse
- EMPTY_CATALOG =
{}.freeze
- WRAPPER =
Methods born from define_method carry their block as a Proc, and Ruby refuses to call them from another Ractor unless that Proc is shareable. Both the handler and the generated wrapper are therefore isolated via Ractor.make_shareable; the wrapper looks its handler up through method and continues traversal explicitly (super is not available inside an isolated proc, and this reproduces Prism::Visitor's default body).
Ractor.make_shareable( proc do |node| handler_for(__method__, node) node.each_child_node { |child| child.accept(self) } end )
Instance Attribute Summary collapse
-
#file ⇒ Object
readonly
Returns the value of attribute file.
-
#findings ⇒ Object
readonly
Returns the value of attribute findings.
Class Method Summary collapse
-
.call(file) ⇒ Array<Finding>
Runs this check over one parsed file.
-
.check_name(value = nil) ⇒ String
No lazy memoization here: checks must be readable from non-main Ractors (parallel scanning), so class-level state is written eagerly on the main Ractor at class definition time and kept deeply shareable.
-
.explain(key, severity:, message:, why:, fix:) ⇒ void
Registers a message catalog entry.
- .explanations ⇒ Object
- .handlers ⇒ Object
-
.on(*node_types) {|node| ... } ⇒ void
Generates visit methods for the given Prism node types.
Instance Method Summary collapse
-
#initialize(file) ⇒ Base
constructor
A new instance of Base.
Constructor Details
#initialize(file) ⇒ Base
Returns a new instance of Base.
121 122 123 124 125 |
# File 'lib/audition/static/checks/base.rb', line 121 def initialize(file) @file = file @findings = [] super() end |
Instance Attribute Details
#file ⇒ Object (readonly)
Returns the value of attribute file.
119 120 121 |
# File 'lib/audition/static/checks/base.rb', line 119 def file @file end |
#findings ⇒ Object (readonly)
Returns the value of attribute findings.
119 120 121 |
# File 'lib/audition/static/checks/base.rb', line 119 def findings @findings end |
Class Method Details
.call(file) ⇒ Array<Finding>
Runs this check over one parsed file.
112 113 114 115 116 |
# File 'lib/audition/static/checks/base.rb', line 112 def call(file) visitor = new(file) visitor.visit(file.root) visitor.findings end |
.check_name(value = nil) ⇒ String
No lazy memoization here: checks must be readable from non-main Ractors (parallel scanning), so class-level state is written eagerly on the main Ractor at class definition time and kept deeply shareable.
40 41 42 43 44 |
# File 'lib/audition/static/checks/base.rb', line 40 def check_name(value = nil) @check_name = -value if value # audition:disable class-level-state @check_name || name.split("::").last .gsub(/([a-z0-9])([A-Z])/, '\1-\2').downcase end |
.explain(key, severity:, message:, why:, fix:) ⇒ void
This method returns an undefined value.
Registers a message catalog entry. Strings may contain
%{placeholders} filled by #flag.
59 60 61 62 63 64 65 66 67 |
# File 'lib/audition/static/checks/base.rb', line 59 def explain(key, severity:, message:, why:, fix:) entry = { severity: severity, message: , why: why, fix: fix } @explanations = Ractor.make_shareable( # audition:disable explanations.merge(key => entry) ) end |
.explanations ⇒ Object
46 47 48 |
# File 'lib/audition/static/checks/base.rb', line 46 def explanations @explanations || EMPTY_CATALOG end |
.handlers ⇒ Object
69 70 71 |
# File 'lib/audition/static/checks/base.rb', line 69 def handlers @handlers || EMPTY_CATALOG end |
.on(*node_types) {|node| ... } ⇒ void
This method returns an undefined value.
Generates visit methods for the given Prism node types. The handler runs via instance_exec and traversal always continues into child nodes afterwards. Handlers must not capture outer locals (they are isolated for cross-Ractor use and Ractor.make_shareable would raise).
97 98 99 100 101 102 103 104 105 106 |
# File 'lib/audition/static/checks/base.rb', line 97 def on(*node_types, &handler) isolated = Ractor.make_shareable(handler) node_types.each do |type| method_name = :"visit_#{type}" @handlers = Ractor.make_shareable( # audition:disable handlers.merge(method_name => isolated) ) define_method(method_name, &WRAPPER) end end |