Module: Rigor::RbsExtended::EnvelopeScanner
- Defined in:
- lib/rigor/rbs_extended/envelope_scanner.rb
Overview
Collects every effect envelope the project's own RBS declares (ADR-103 WD5 / WD14; #383).
It produces values, never diagnostics — the check that compares a bound against the proven summaries is Effects::EnvelopeCheck. Two tables come out, because they are consumed differently:
- Result#method_envelopes — keyed by method key (
Class#m/Class.m), applied as written. - Result#class_envelopes — keyed by class / module name, DISTRIBUTED by the check to every
method of that Ruby class discovery knows (reopenings, other files, synthesized
attr_*anddefine_methodmembers), never to subclasses. A per-method envelope wins over a distributed one; nearest wins, and no-exceptsyntax is needed.
Why it parses rather than reading the built environment
ConformanceChecker, the obvious model, walks RbsLoader's built env. An envelope cannot: the
diagnostic has to name where the bound was written (sig/foo.rbs:12), and the ADR-54 env
cache dumps every RBS::Location to a zero-range <cached> sentinel, so a warm run would lose
both the position and the only evidence of which file a declaration came from. Parsing the
project's own signature sources is a few milliseconds over a tree Rigor already globs, it is
identical warm and cold, and it enforces ADR-103 WD6's trust rule structurally: a %a{pure} in
rbs core or in a gem's shipped RBS is never read, so it cannot bound a project method that
happens to share its key.
Malformed payloads and pure-versus-effect contradictions are recorded on a Reporter the
scanner owns and ride out on Result#unresolved. They surface no diagnostic in this slice:
effect.unknown-label and the annotation-in-a-project-without-effects: residual are #384's.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- EMPTY =
Result.new(method_envelopes: {}.freeze, class_envelopes: {}.freeze, unresolved: [].freeze)
Class Method Summary collapse
-
.from_loader(loader:, registry:) ⇒ Hash{String => Rigor::Effects::Envelope}
The accepted-signature reader: every per-method envelope declared anywhere in a BUILT
RBS::Environment— a gem's shipped signatures, Rigor's bundled overlays, core RBS (ADR-103 WD6; #386). - .scan(sources:, registry:) ⇒ Result
Class Method Details
.from_loader(loader:, registry:) ⇒ Hash{String => Rigor::Effects::Envelope}
The accepted-signature reader: every per-method envelope declared anywhere in a BUILT
RBS::Environment — a gem's shipped signatures, Rigor's bundled overlays, core RBS
(ADR-103 WD6; #386).
It exists for one purpose and is usable for exactly one: importing a ≤ bound at a call site
that resolves into un-analysed code (Effects::EnvelopeIndex). It MUST NOT feed a
contract check. Two facts make that a structural guarantee rather than a convention —
scan, which the check reads, never opens this door, and the values here carry no
location at all (the ADR-54 env cache dumps every RBS::Location to a <cached> sentinel,
so a warm run could not name where the bound was written even if a diagnostic wanted to).
There is also no body to check: a gem method is un-analysed by definition, which is why WD6
trusts the declaration instead, exactly as it already trusts the gem's types.
Class-level annotations are deliberately not read. Distribution needs "every method of the class discovery knows", which is a project fact; a gem's class-level tag would have to distribute over a definition set this reader does not have.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/rigor/rbs_extended/envelope_scanner.rb', line 92 def from_loader(loader:, registry:) out = {} loader.each_annotated_method_member do |class_name, member| keys_for(class_name, member).each do |key| next if out.key?(key) envelope = RbsExtended.read_effect_envelope( member.annotations, owner_key: key, source: :effect_annotation, registry: registry ) out[key] = envelope if envelope end end out.freeze rescue StandardError {}.freeze end |
.scan(sources:, registry:) ⇒ Result
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/rigor/rbs_extended/envelope_scanner.rb', line 53 def scan(sources:, registry:) reporter = Reporter.new methods = {} classes = {} sources.each do |name, content| declarations(name, content).each do |decl| walk(decl, [], methods, classes, registry, reporter) end end Result.new( method_envelopes: methods.freeze, class_envelopes: classes.freeze, unresolved: reporter.unresolved_payloads ) rescue StandardError EMPTY end |