Module: Rigor::RbsExtended

Defined in:
lib/rigor/rbs_extended.rb

Overview

Slice 7 phase 15 — first-preview reader for the ‘RBS::Extended` annotation surface described in `docs/type-specification/rbs-extended.md`.

This module reads ‘%a<payload>` annotations off RBS method definitions and returns well-typed effect objects the inference engine can consume. v0.0.2 recognises:

  • ‘rigor:v1:predicate-if-true <target> is <ClassName>`

  • ‘rigor:v1:predicate-if-false <target> is <ClassName>`

  • ‘rigor:v1:assert <target> is <ClassName>`

  • ‘rigor:v1:assert-if-true <target> is <ClassName>`

  • ‘rigor:v1:assert-if-false <target> is <ClassName>`

‘predicate-if-*` fires when the call is used as an `if` / `unless` condition; `assert` fires unconditionally at the call’s post-scope; ‘assert-if-true` / `assert-if-false` fire at the post-scope only when the call’s return value can be observed as truthy / falsey (currently: when the call is the predicate of a subsequent ‘if` / `unless`). Other directives in the spec (`param`, `return`, `conforms-to`, negation `~T`, `target: self` narrowing, …) remain on the v0.0.x roadmap. Annotations whose key is in the `rigor:v1:` namespace but whose directive is unrecognised are silently ignored at first-preview quality (a future slice MAY surface them as diagnostics-on-Rigor-itself per the spec’s “unsupported metadata” guidance).

The parser is minimal: it accepts a strict shape ‘<target> is <ClassName>` where `<target>` is a Ruby identifier (parameter name) or `self`, and `<ClassName>` is a single non-namespaced class identifier or a `::Foo::Bar` style constant path. Negative refinements (`~T`), intersections, and unions are deferred to the next iteration.

Defined Under Namespace

Classes: AssertEffect, PredicateEffect

Constant Summary collapse

DIRECTIVE_PREFIX =
"rigor:v1:"

Class Method Summary collapse

Class Method Details

.parse_assert_annotation(string) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/rigor/rbs_extended.rb', line 173

def parse_assert_annotation(string)
  match = ASSERT_DIRECTIVE_PATTERN.match(string)
  return nil if match.nil?

  directive = match[:directive].to_s
  condition = ASSERT_CONDITIONS[directive]
  return nil if condition.nil?

  target = match[:target].to_s
  class_name = match[:class_name].to_s.sub(/\A::/, "")
  target_kind = target == "self" ? :self : :parameter
  target_name = target == "self" ? :self : target.to_sym
  AssertEffect.new(
    condition: condition,
    target_kind: target_kind,
    target_name: target_name,
    class_name: class_name,
    negative: match[:negation].to_s == "~"
  )
end

.parse_predicate_annotation(string) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rigor/rbs_extended.rb', line 115

def parse_predicate_annotation(string)
  match = PREDICATE_DIRECTIVE_PATTERN.match(string)
  return nil if match.nil?

  directive = match[:directive].to_s
  target = match[:target].to_s
  class_name = match[:class_name].to_s.sub(/\A::/, "")
  edge = directive == "predicate-if-true" ? :truthy_only : :falsey_only
  target_kind = target == "self" ? :self : :parameter
  target_name = target == "self" ? :self : target.to_sym
  PredicateEffect.new(
    edge: edge,
    target_kind: target_kind,
    target_name: target_name,
    class_name: class_name,
    negative: match[:negation].to_s == "~"
  )
end

.read_assert_effects(method_def) ⇒ Object

Reads RBS::Extended assertion effects (‘assert`, `assert-if-true`, `assert-if-false`) off `RBS::Definition::Method#annotations`. Returns an empty array when no recognised assertion directives are attached to the method.



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/rigor/rbs_extended.rb', line 139

def read_assert_effects(method_def)
  return [] if method_def.nil?

  annotations = method_def.annotations
  return [] if annotations.nil? || annotations.empty?

  effects = []
  annotations.each do |annotation|
    effect = parse_assert_annotation(annotation.string)
    effects << effect if effect
  end
  effects.uniq
end

.read_predicate_effects(method_def) ⇒ Object

Reads RBS::Extended predicate effects off ‘RBS::Definition::Method#annotations`. Returns the effects in source order; duplicates and unrecognised `rigor:v1:` directives are dropped. Returns an empty array (NEVER `nil`) for a method with no recognised annotations so callers can iterate unconditionally.



88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rigor/rbs_extended.rb', line 88

def read_predicate_effects(method_def)
  return [] if method_def.nil?

  annotations = method_def.annotations
  return [] if annotations.nil? || annotations.empty?

  effects = []
  annotations.each do |annotation|
    effect = parse_predicate_annotation(annotation.string)
    effects << effect if effect
  end
  effects.uniq
end