Module: Rigor::Inference::Narrowing

Defined in:
lib/rigor/inference/narrowing.rb,
sig/rigor/inference.rbs

Overview

Control-flow predicate narrowing and type-lattice narrowing primitives.

Rigor::Inference::Narrowing answers two related questions:

  1. Type-level narrowing: given a Rigor::Type value, what is its truthy fragment, its falsey fragment, its nil fragment, and its non-nil fragment? These primitives understand the value-lattice algebra (Constant, Nominal, Singleton, Tuple, HashShape, Union) and stay conservative on Top and Dynamic[T].
  2. Predicate-level narrowing: given a Prism predicate node and an entry scope, what are the truthy-edge scope and the falsey-edge scope? The catalogue covers truthiness, nil?, !, &&/||, class-membership (is_a?, kind_of?, instance_of?), trusted equality/inequality against static literals, case/when, regex match globals, string predicates (start_with? etc.), key-presence, array emptiness, numeric comparison, and respond_to?.

Consumed by Rigor::Inference::StatementEvaluator to refine then/else scopes of IfNode/UnlessNode and case/when branches.

The module is pure: every public function returns fresh values and MUST NOT mutate its inputs. Unrecognised predicate shapes degrade silently to "no narrowing" by returning nil from the internal analyser; the public predicate_scopes always returns an [truthy_scope, falsey_scope] pair (the entry scope twice when no rule matches).

See docs/internal-spec/inference-engine.md (Narrowing) and docs/type-specification/control-flow-analysis.md for the binding contract. rubocop:disable Metrics/ModuleLength

Constant Summary collapse

VALUE_EQUALITY_CLASSES =

Classes whose === is plain value equality, so a literal when pattern against a pinned Constant subject is exact in both directions. Anything else keeps custom-=== semantics and stays :maybe in value_pattern_certainty.

Returns:

  • (Array[Class])
[Integer, Float, Rational, Complex, String, Symbol,
TrueClass, FalseClass, NilClass].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.analyse(node, scope) ⇒ Object

Internal analyser. Returns [truthy_scope, falsey_scope] when the predicate shape is recognised, or nil to signal "no narrowing" so the public surface can fall back to the entry scope.



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/rigor/inference/narrowing.rb', line 415

def analyse(node, scope)
  case node
  when Prism::ParenthesesNode
    analyse_parentheses(node, scope)
  when Prism::StatementsNode
    analyse_statements(node, scope)
  when Prism::LocalVariableReadNode
    analyse_local_read(node, scope)
  when Prism::LocalVariableWriteNode
    analyse_local_write(node, scope)
  when Prism::InstanceVariableReadNode, Prism::InstanceVariableWriteNode
    analyse_ivar(node, scope)
  when Prism::ClassVariableWriteNode
    analyse_cvar_write(node, scope)
  when Prism::GlobalVariableWriteNode
    analyse_global_write(node, scope)
  when Prism::CallNode
    analyse_call(node, scope)
  when Prism::AndNode
    analyse_and(node, scope)
  when Prism::OrNode
    analyse_or(node, scope)
  when Prism::MatchWriteNode
    analyse_match_write(node, scope)
  end
end

.apply_when_regex_globals(conditions, scope) ⇒ Object

When the clause has exactly one RegularExpressionNode literal condition, narrow the match-data globals on the body edge (same rule as analyse_regex_match_predicate's truthy edge). With multiple regex conditions (when /a/, /b/) the body is reachable through any of them, so only $~/$& are safely non-nil; numbered groups whose presence differs per alternative stay String | nil. With no regex condition the entry scope passes through unchanged.



398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/rigor/inference/narrowing.rb', line 398

def apply_when_regex_globals(conditions, scope)
  regexes = conditions.grep(Prism::RegularExpressionNode)
  return scope if regexes.empty?

  unconditional =
    if regexes.size == 1
      unconditional_capture_groups(regexes.first.unescaped)
    else
      Set.new
    end
  truthy, = regex_match_predicate_scopes(scope, unconditional)
  truthy
end

.case_when_scopes(subject, conditions, scope) ⇒ Array(Rigor::Scope, Rigor::Scope)

Slice 7 phase 5 — case/when narrowing.

Given the subject of a case (the expression after the case keyword) and an array of when-clause condition nodes (when_clause.conditions), returns a pair of scopes:

  • body_scope: the scope under which the body of the when clause MUST be evaluated. The subject local is narrowed by the union of every condition's truthy edge so the body sees the most specific type compatible with "any of the conditions matched".
  • falsey_scope: the scope under which the next branch (the next when or the else) MUST be evaluated. The subject is narrowed by the conjunction of every condition's falsey edge.

The narrowing is best-effort: if the subject is not a Prism::LocalVariableReadNode or none of the condition shapes are recognised, both returned scopes equal the input scope. The catalogue mirrors case_equality_target_class: static class/module constants narrow as is_a?; integer/float-endpoint ranges narrow to Numeric; string-endpoint ranges and regexp literals narrow to String.

Parameters:

Returns:



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/rigor/inference/narrowing.rb', line 374

def case_when_scopes(subject, conditions, scope)
  # C1 — `case x when /re/` runs `/re/ === x`, which sets the regex match-data globals
  # exactly as a successful `=~` does. Narrow `$~`/`$&`/`$1..$N` on the clause body (the
  # match edge); the falsey scope keeps the entry globals because a later clause may match
  # a different regex. Applied even when the subject is not a narrowable local read.
  body_scope = apply_when_regex_globals(conditions, scope)

  return [body_scope, scope] unless subject.is_a?(Prism::LocalVariableReadNode)

  local_name = subject.name
  current = scope.local(local_name)
  return [body_scope, scope] if current.nil?

  truthy, = accumulate_case_when_scopes(body_scope, local_name, current, conditions)
  _, falsey = accumulate_case_when_scopes(scope, local_name, current, conditions)
  [truthy, falsey]
end

.class_pattern_certainty(subject_type, class_name, environment:) ⇒ Object

Three-valued certainty of C === subject for a class / module when pattern, derived from narrow_class / narrow_not_class: :no when no inhabitant of the subject matches, :yes when every inhabitant matches, :maybe otherwise. The value-side counterpart of the scope narrowing case_when_scopes performs for the same condition shape, kept here so the branch a case expression's type drops and the clause whose body scope goes dead derive from one judgment.



142
143
144
145
146
147
148
149
150
# File 'lib/rigor/inference/narrowing.rb', line 142

def class_pattern_certainty(subject_type, class_name, environment:)
  truthy_bot = narrow_class(subject_type, class_name, environment: environment).is_a?(Type::Bot)
  falsey_bot = narrow_not_class(subject_type, class_name, environment: environment).is_a?(Type::Bot)

  return :no if truthy_bot && !falsey_bot
  return :yes if !truthy_bot && falsey_bot

  :maybe
end

.narrow_class(type, class_name, exact: false, environment: Environment.default) ⇒ Object

Class-membership fragment of type: the subset whose inhabitants are instances of class_name (or its subclasses when exact: false). class_name is the qualified name of the class as it appears in source ("Integer", "Foo::Bar"). Slice 6 phase 2 sub-phase 1 narrows the if x.is_a?(C) / if x.kind_of?(C) / if x.instance_of?(C) truthy edge.

Nominal narrowing is hierarchy-aware through the analyzer environment: when the bound type is a supertype of class_name the result narrows DOWN to Nominal[class_name] (e.g., Numeric & Integer = Integer); when the bound type is already a subtype it is preserved; disjoint hierarchies collapse to Bot. Classes the environment cannot resolve fall back to the conservative answer (the type unchanged) so the analyzer never asserts narrowing it cannot prove.



264
265
266
267
# File 'lib/rigor/inference/narrowing.rb', line 264

def narrow_class(type, class_name, exact: false, environment: Environment.default)
  context = ClassNarrowingContext.new(exact: exact, polarity: :positive, environment: environment)
  narrow_class_dispatch(type, class_name, context)
end

.narrow_equal(type, literal) ⇒ Object

Equality fragment of type against a trusted literal.

String/Symbol/Integer equality narrows only when the current domain is already a finite union of trusted literals. Nil and booleans are singleton values, so they can be extracted from a mixed union such as Integer | nil without manufacturing a new positive domain from the comparison alone.



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rigor/inference/narrowing.rb', line 175

def narrow_equal(type, literal)
  return type unless trusted_equality_literal?(literal)

  if singleton_literal?(literal)
    narrow_singleton_equal(type, literal)
  elsif finite_trusted_literal_domain?(type)
    narrow_finite_equal(type, literal)
  else
    type
  end
end

.narrow_falsey(type) ⇒ Object

Falsey fragment of type: the subset whose inhabitants are nil or false. Carriers that cannot inhabit a falsey value collapse to Bot.



77
78
79
80
81
82
83
84
# File 'lib/rigor/inference/narrowing.rb', line 77

def narrow_falsey(type)
  case type
  when Type::Constant then falsey_value?(type.value) ? type : Type::Combinator.bot
  when Type::Nominal then falsey_nominal?(type) ? type : Type::Combinator.bot
  when Type::Union then Type::Combinator.union(*type.members.map { |m| narrow_falsey(m) })
  else narrow_falsey_other(type)
  end
end

.narrow_for_fact(current, fact, environment) ⇒ Object

ADR-7 § "Slice 4-A" — public Fact-shaped narrowing entry. Distinguishes a Nominal[<class>]-typed Fact (uses narrow_class / narrow_not_class for hierarchy-aware narrowing) from a refinement-shaped Fact (refined types, IntegerRange, Difference, …). The implementation lives next to its sibling helpers narrow_class and narrow_not_refinement; consumers outside Narrowing (today: StatementEvaluator's post-return assertion path) reach for it via Rigor::Inference::Narrowing.narrow_for_fact.



325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/rigor/inference/narrowing.rb', line 325

def narrow_for_fact(current, fact, environment)
  if fact.type.is_a?(Type::Nominal) && fact.type.type_args.empty?
    class_name = fact.type.class_name
    return narrow_not_class(current, class_name, exact: false, environment: environment) if fact.negative?

    return narrow_class(current, class_name, exact: false, environment: environment)
  end

  return narrow_not_refinement(current, fact.type) if fact.negative?

  fact.type
end

.narrow_integer_comparison(type, comparator, bound) ⇒ Object

Integer-comparison fragment of type against an Integer literal bound. Narrows the receiver of x < n, x <= n, x > n, x >= n (and the reversed forms) to the subset of the existing domain that satisfies the comparison. Hooks in:

  • Constant<Integer> is preserved when it satisfies the comparison, otherwise collapsed to Bot.
  • IntegerRange[a..b] becomes the intersection with the half-line implied by the comparison; an empty intersection collapses to Bot, a single-point intersection collapses to Constant<Integer>.
  • Nominal[Integer] becomes the half-line itself (e.g. x > 0 on Nominal[Integer] is positive_int).
  • Union narrows each member independently.
  • Other carriers (Float, String, Top, Dynamic) flow through unchanged: the analyzer does not have a Float-range carrier today, and no other carrier participates in numeric ordering.


216
217
218
219
220
# File 'lib/rigor/inference/narrowing.rb', line 216

def narrow_integer_comparison(type, comparator, bound)
  return type unless bound.is_a?(Integer) && %i[< <= > >=].include?(comparator)

  narrow_integer_comparison_dispatch(type, comparator, bound)
end

.narrow_integer_equal(type, value) ⇒ Object

Equality fragment of type against an Integer value. Constant<Integer> is preserved when it equals value, otherwise collapses to Bot. IntegerRange covers? value narrows to Constant[value]; an out-of-range comparison collapses to Bot. Nominal[Integer] narrows to Constant[value]. Union narrows each member.



226
227
228
229
230
# File 'lib/rigor/inference/narrowing.rb', line 226

def narrow_integer_equal(type, value)
  return type unless value.is_a?(Integer)

  narrow_integer_equal_dispatch(type, value)
end

.narrow_integer_not_equal(type, value) ⇒ Object

Complement of narrow_integer_equal. Removes a single integer value from the domain when one endpoint of an IntegerRange is exactly that value (so the result stays a contiguous range). Domains where the value sits strictly between the endpoints stay unchanged: punching a hole would require a two-piece carrier the lattice does not yet model.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/rigor/inference/narrowing.rb', line 237

def narrow_integer_not_equal(type, value)
  return type unless value.is_a?(Integer)

  case type
  when Type::Constant
    type.value == value ? Type::Combinator.bot : type
  when Type::IntegerRange
    narrow_integer_range_not_equal(type, value)
  when Type::Union
    Type::Combinator.union(*type.members.map { |m| narrow_integer_not_equal(m, value) })
  else
    type
  end
end

.narrow_nil(type) ⇒ Object

Nil fragment of type: the subset whose inhabitants are nil. Used by nil? predicate narrowing. Top/Dynamic narrow to the canonical Constant[nil] so downstream dispatch resolves through NilClass; carriers that never inhabit nil (Singleton, Tuple, HashShape) collapse to Bot. Bot is its own nil fragment.



90
91
92
93
94
95
96
97
# File 'lib/rigor/inference/narrowing.rb', line 90

def narrow_nil(type)
  case type
  when Type::Constant then type.value.nil? ? type : Type::Combinator.bot
  when Type::Nominal then type.class_name == "NilClass" ? type : Type::Combinator.bot
  when Type::Union then Type::Combinator.union(*type.members.map { |m| narrow_nil(m) })
  else narrow_nil_other(type)
  end
end

.narrow_non_nil(type) ⇒ Object

Non-nil fragment of type: the subset whose inhabitants are not nil. Mirror of narrow_nil for the falsey edge of x.nil?.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rigor/inference/narrowing.rb', line 101

def narrow_non_nil(type)
  case type
  when Type::Constant
    type.value.nil? ? Type::Combinator.bot : type
  when Type::Nominal
    type.class_name == "NilClass" ? Type::Combinator.bot : type
  when Type::Union
    Type::Combinator.union(*type.members.map { |m| narrow_non_nil(m) })
  else
    # Top, Dynamic, Singleton, Tuple, HashShape, Bot: there is no nil contribution to
    # remove, so the type is its own non-nil fragment.
    type
  end
end

.narrow_not_class(type, class_name, exact: false, environment: Environment.default) ⇒ Object

Mirror of narrow_class for the falsey edge of is_a?/kind_of?/instance_of?. Inhabitants that DO satisfy the predicate are removed; inhabitants that do not are preserved. Conservative on Top/Dynamic/Bot (preserved unchanged) because the analyzer cannot prove the negative without a richer carrier.



273
274
275
276
# File 'lib/rigor/inference/narrowing.rb', line 273

def narrow_not_class(type, class_name, exact: false, environment: Environment.default)
  context = ClassNarrowingContext.new(exact: exact, polarity: :negative, environment: environment)
  narrow_class_dispatch(type, class_name, context)
end

.narrow_not_equal(type, literal) ⇒ Object

Complement of narrow_equal. Negative facts are domain-relative: they remove a literal from an already-known domain but do not create an unbounded difference type when the domain is broad or dynamic.



190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rigor/inference/narrowing.rb', line 190

def narrow_not_equal(type, literal)
  return type unless trusted_equality_literal?(literal)

  if singleton_literal?(literal)
    narrow_singleton_not_equal(type, literal)
  elsif finite_trusted_literal_domain?(type)
    narrow_finite_not_equal(type, literal)
  else
    type
  end
end

.narrow_not_refinement(current_type, refinement_type) ⇒ Object

Negation pair for assert_value is ~refinement / predicate-if-* … is ~refinement directives. Computes the complement of refinement within the current local's domain current_type.

Carrier-by-carrier rules:

  • Difference[base, Constant[v]]. Complement of base \ {v} within current_type. Walk the current type's union members, keep each part disjoint from base, and add the removed-value Constant once when any current member covers it. assert s is ~non-empty-string over s: String | nil narrows to Constant[""] | NilClass.
  • IntegerRange[a, b] (v0.0.5+ slice). Complement is the two open halves int<min, a-1> and int<b+1, max>, each intersected with the integer-domain parts of current_type. Non-integer parts (nil, String, …) of a Union receiver survive unchanged. assert n is ~int<5, 10> over n: Integer | nil narrows to int<min, 4> | int<11, max> | NilClass.
  • Type::Intersection[M1, M2, …] (v0.0.5+ slice). De Morgan: D \ (M1 ∩ M2) = (D \ M1) ∪ (D \ M2). Each member's complement is computed independently within current_type and the results are unioned. Members the algebra cannot complement (Refined, non-Constant Difference, …) contribute current_type itself, so the union widens the answer to current_type — sound but imprecise.
  • Refined[base, predicate]. Predicate complements are not reducible to a finite carrier without a richer shape (e.g. ~lowercase-string is "uppercase OR mixed-case"); current_type is returned unchanged.


301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/rigor/inference/narrowing.rb', line 301

def narrow_not_refinement(current_type, refinement_type)
  case refinement_type
  when Type::Difference
    return current_type unless refinement_type.removed.is_a?(Type::Constant)

    complement_difference(current_type, refinement_type)
  when Type::IntegerRange
    complement_integer_range(current_type, refinement_type)
  when Type::Intersection
    complement_intersection(current_type, refinement_type)
  when Type::Refined
    complement_refined(current_type, refinement_type)
  else
    current_type
  end
end

.narrow_truthy(type) ⇒ Object

Truthy fragment of type: the subset whose inhabitants are truthy in Ruby's sense (anything other than nil and false).

Top, Dynamic[T], Bot, Singleton[C], Tuple[*], and HashShape{*} flow through unchanged: Top/Dynamic stay conservative because the analyzer cannot express the difference type without a richer carrier and Dynamic must preserve its provenance under the value-lattice algebra; the remaining carriers are already truthy by inhabitance.



62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rigor/inference/narrowing.rb', line 62

def narrow_truthy(type)
  case type
  when Type::Constant
    falsey_value?(type.value) ? Type::Combinator.bot : type
  when Type::Nominal
    falsey_nominal?(type) ? Type::Combinator.bot : type
  when Type::Union
    Type::Combinator.union(*type.members.map { |m| narrow_truthy(m) })
  else
    type
  end
end

.predicate_certainty(type) ⇒ Object

Three-valued truthiness certainty of a predicate's type, derived from the truthy / falsey fragments above: :truthy when no inhabitant is falsey (the falsey fragment is Bot), :falsey when no inhabitant is truthy, nil when both fragments are inhabited — or when the type itself is nil / Bot (dead code is not a certainty claim). This is the single owner of the judgment both branch-elision consumers read (ExpressionTyper#elide_or_union on the value side, StatementEvaluator#live_branch_for_if on the scope side), so the type a dead branch is elided from and the scope that stops flowing through it can never disagree.



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rigor/inference/narrowing.rb', line 124

def predicate_certainty(type)
  return nil if type.nil? || type.is_a?(Type::Bot)

  truthy_bot = narrow_truthy(type).is_a?(Type::Bot)
  falsey_bot = narrow_falsey(type).is_a?(Type::Bot)

  return :falsey if truthy_bot && !falsey_bot
  return :truthy if !truthy_bot && falsey_bot

  nil
end

.predicate_scopes(node, scope) ⇒ Array(Rigor::Scope, Rigor::Scope)

Public predicate analyser. Returns [truthy_scope, falsey_scope], always; when no narrowing rule matches the predicate node both entries are the receiver scope unchanged.

Parameters:

Returns:



344
345
346
347
348
349
# File 'lib/rigor/inference/narrowing.rb', line 344

def predicate_scopes(node, scope)
  return [scope, scope] if node.nil?

  result = analyse(node, scope)
  result || [scope, scope]
end

.value_pattern_certainty(subject_type, pattern_value) ⇒ Object

Three-valued certainty of <literal> === subject for a value-equality literal pattern: exact (:yes / :no) only when the subject is itself a pinned Constant of a value-equality class; :maybe otherwise (the runtime value isn't pinned, or === may be user-defined).



162
163
164
165
166
167
# File 'lib/rigor/inference/narrowing.rb', line 162

def value_pattern_certainty(subject_type, pattern_value)
  return :maybe unless subject_type.is_a?(Type::Constant)
  return :maybe unless VALUE_EQUALITY_CLASSES.any? { |klass| subject_type.value.is_a?(klass) }

  pattern_value == subject_type.value ? :yes : :no
end

Instance Method Details

#self?.analyseObject

Parameters:

  • node (Object)
  • scope (Scope)

Returns:

  • (Object)


120
# File 'sig/rigor/inference.rbs', line 120

def self?.analyse: (untyped node, Scope scope) -> untyped

#self?.case_when_scopes[Scope, Scope]

Parameters:

  • subject (Object)
  • conditions (Array[untyped])
  • scope (Scope)

Returns:



119
# File 'sig/rigor/inference.rbs', line 119

def self?.case_when_scopes: (untyped subject, Array[untyped] conditions, Scope scope) -> [Scope, Scope]

#self?.class_orderingSymbol

Parameters:

  • lhs (String)
  • rhs (String)
  • context (Object)

Returns:

  • (Symbol)


122
# File 'sig/rigor/inference.rbs', line 122

def self?.class_ordering: (String lhs, String rhs, untyped context) -> Symbol

#self?.class_pattern_certainty:yes, ...

Parameters:

  • subject_type (Type::t)
  • class_name (String)
  • environment: (Environment)

Returns:

  • (:yes, :no, :maybe)


116
# File 'sig/rigor/inference.rbs', line 116

def self?.class_pattern_certainty: (Type::t subject_type, String class_name, environment: Environment) -> (:yes | :no | :maybe)

#self?.falsey_nominal?Boolean

Parameters:

Returns:

  • (Boolean)


125
# File 'sig/rigor/inference.rbs', line 125

def self?.falsey_nominal?: (Type::Nominal nominal) -> bool

#self?.falsey_value?Boolean

Parameters:

  • value (Object)

Returns:

  • (Boolean)


124
# File 'sig/rigor/inference.rbs', line 124

def self?.falsey_value?: (untyped value) -> bool

#self?.narrow_classType::t

Parameters:

  • type (Type::t)
  • class_name (String)
  • exact: (Boolean)
  • environment: (Environment)

Returns:

  • (Type::t)


111
# File 'sig/rigor/inference.rbs', line 111

def self?.narrow_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t

#self?.narrow_equalType::t

Parameters:

  • type (Type::t)
  • literal (Object)

Returns:

  • (Type::t)


109
# File 'sig/rigor/inference.rbs', line 109

def self?.narrow_equal: (Type::t type, untyped literal) -> Type::t

#self?.narrow_falseyType::t

Parameters:

  • type (Type::t)

Returns:

  • (Type::t)


106
# File 'sig/rigor/inference.rbs', line 106

def self?.narrow_falsey: (Type::t type) -> Type::t

#self?.narrow_for_factType::t

Parameters:

  • current (Type::t)
  • fact (Object)
  • environment (Object)

Returns:

  • (Type::t)


114
# File 'sig/rigor/inference.rbs', line 114

def self?.narrow_for_fact: (Type::t current, untyped fact, untyped environment) -> Type::t

#self?.narrow_nilType::t

Parameters:

  • type (Type::t)

Returns:

  • (Type::t)


107
# File 'sig/rigor/inference.rbs', line 107

def self?.narrow_nil: (Type::t type) -> Type::t

#self?.narrow_non_nilType::t

Parameters:

  • type (Type::t)

Returns:

  • (Type::t)


108
# File 'sig/rigor/inference.rbs', line 108

def self?.narrow_non_nil: (Type::t type) -> Type::t

#self?.narrow_not_classType::t

Parameters:

  • type (Type::t)
  • class_name (String)
  • exact: (Boolean)
  • environment: (Environment)

Returns:

  • (Type::t)


112
# File 'sig/rigor/inference.rbs', line 112

def self?.narrow_not_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t

#self?.narrow_not_equalType::t

Parameters:

  • type (Type::t)
  • literal (Object)

Returns:

  • (Type::t)


110
# File 'sig/rigor/inference.rbs', line 110

def self?.narrow_not_equal: (Type::t type, untyped literal) -> Type::t

#self?.narrow_not_refinementType::t

Parameters:

  • current_type (Type::t)
  • refinement_type (Type::t)

Returns:

  • (Type::t)


113
# File 'sig/rigor/inference.rbs', line 113

def self?.narrow_not_refinement: (Type::t current_type, Type::t refinement_type) -> Type::t

#self?.narrow_truthyType::t

Parameters:

  • type (Type::t)

Returns:

  • (Type::t)


105
# File 'sig/rigor/inference.rbs', line 105

def self?.narrow_truthy: (Type::t type) -> Type::t

#self?.predicate_certainty:truthy, ...

Parameters:

  • type (Type::t, nil)

Returns:

  • (:truthy, :falsey, nil)


115
# File 'sig/rigor/inference.rbs', line 115

def self?.predicate_certainty: (Type::t? type) -> (:truthy | :falsey | nil)

#self?.predicate_scopes[Scope, Scope]

Parameters:

  • node (Object)
  • scope (Scope)

Returns:



118
# File 'sig/rigor/inference.rbs', line 118

def self?.predicate_scopes: (untyped node, Scope scope) -> [Scope, Scope]

#self?.subclass_of?Boolean

Parameters:

  • rigor_class_name (String)
  • target_class_name (String)
  • context (Object)

Returns:

  • (Boolean)


121
# File 'sig/rigor/inference.rbs', line 121

def self?.subclass_of?: (String rigor_class_name, String target_class_name, untyped context) -> bool

#self?.trusted_equality_literal?Boolean

Parameters:

  • literal (Object)

Returns:

  • (Boolean)


123
# File 'sig/rigor/inference.rbs', line 123

def self?.trusted_equality_literal?: (untyped literal) -> bool

#self?.value_pattern_certainty:yes, ...

Parameters:

  • subject_type (Type::t)
  • pattern_value (Object)

Returns:

  • (:yes, :no, :maybe)


117
# File 'sig/rigor/inference.rbs', line 117

def self?.value_pattern_certainty: (Type::t subject_type, untyped pattern_value) -> (:yes | :no | :maybe)