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:
- Type-level narrowing: given a
Rigor::Typevalue, 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 onTopandDynamic[T]. - 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, andrespond_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 literalwhenpattern against a pinnedConstantsubject is exact in both directions. Anything else keeps custom-===semantics and stays:maybein value_pattern_certainty. [Integer, Float, Rational, Complex, String, Symbol, TrueClass, FalseClass, NilClass].freeze
Class Method Summary collapse
-
.analyse(node, scope) ⇒ Object
Internal analyser.
-
.apply_when_regex_globals(conditions, scope) ⇒ Object
When the clause has exactly one
RegularExpressionNodeliteral condition, narrow the match-data globals on the body edge (same rule asanalyse_regex_match_predicate's truthy edge). -
.case_when_scopes(subject, conditions, scope) ⇒ Array(Rigor::Scope, Rigor::Scope)
Slice 7 phase 5 —
case/whennarrowing. -
.class_pattern_certainty(subject_type, class_name, environment:) ⇒ Object
Three-valued certainty of
C === subjectfor a class / modulewhenpattern, derived from Narrowing.narrow_class / Narrowing.narrow_not_class::nowhen no inhabitant of the subject matches,:yeswhen every inhabitant matches,:maybeotherwise. -
.narrow_class(type, class_name, exact: false, environment: Environment.default) ⇒ Object
Class-membership fragment of
type: the subset whose inhabitants are instances ofclass_name(or its subclasses whenexact: false). -
.narrow_equal(type, literal) ⇒ Object
Equality fragment of
typeagainst a trusted literal. -
.narrow_falsey(type) ⇒ Object
Falsey fragment of
type: the subset whose inhabitants arenilorfalse. -
.narrow_for_fact(current, fact, environment) ⇒ Object
ADR-7 § "Slice 4-A" — public Fact-shaped narrowing entry.
-
.narrow_integer_comparison(type, comparator, bound) ⇒ Object
Integer-comparison fragment of
typeagainst an Integer literalbound. -
.narrow_integer_equal(type, value) ⇒ Object
Equality fragment of
typeagainst an Integervalue. -
.narrow_integer_not_equal(type, value) ⇒ Object
Complement of Narrowing.narrow_integer_equal.
-
.narrow_nil(type) ⇒ Object
Nil fragment of
type: the subset whose inhabitants arenil. -
.narrow_non_nil(type) ⇒ Object
Non-nil fragment of
type: the subset whose inhabitants are notnil. -
.narrow_not_class(type, class_name, exact: false, environment: Environment.default) ⇒ Object
Mirror of Narrowing.narrow_class for the falsey edge of
is_a?/kind_of?/instance_of?. -
.narrow_not_equal(type, literal) ⇒ Object
Complement of Narrowing.narrow_equal.
-
.narrow_not_refinement(current_type, refinement_type) ⇒ Object
Negation pair for
assert_value is ~refinement/predicate-if-* … is ~refinementdirectives. -
.narrow_truthy(type) ⇒ Object
Truthy fragment of
type: the subset whose inhabitants are truthy in Ruby's sense (anything other thannilandfalse). -
.predicate_certainty(type) ⇒ Object
Three-valued truthiness certainty of a predicate's type, derived from the truthy / falsey fragments above:
:truthywhen no inhabitant is falsey (the falsey fragment isBot),:falseywhen 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). -
.predicate_scopes(node, scope) ⇒ Array(Rigor::Scope, Rigor::Scope)
Public predicate analyser.
-
.value_pattern_certainty(subject_type, pattern_value) ⇒ Object
Three-valued certainty of
<literal> === subjectfor a value-equality literal pattern: exact (:yes/:no) only when the subject is itself a pinnedConstantof a value-equality class;:maybeotherwise (the runtime value isn't pinned, or===may be user-defined).
Instance Method Summary collapse
- #self?.analyse ⇒ Object
- #self?.case_when_scopes ⇒ [Scope, Scope]
- #self?.class_ordering ⇒ Symbol
- #self?.class_pattern_certainty ⇒ :yes, ...
- #self?.falsey_nominal? ⇒ Boolean
- #self?.falsey_value? ⇒ Boolean
- #self?.narrow_class ⇒ Type::t
- #self?.narrow_equal ⇒ Type::t
- #self?.narrow_falsey ⇒ Type::t
- #self?.narrow_for_fact ⇒ Type::t
- #self?.narrow_nil ⇒ Type::t
- #self?.narrow_non_nil ⇒ Type::t
- #self?.narrow_not_class ⇒ Type::t
- #self?.narrow_not_equal ⇒ Type::t
- #self?.narrow_not_refinement ⇒ Type::t
- #self?.narrow_truthy ⇒ Type::t
- #self?.predicate_certainty ⇒ :truthy, ...
- #self?.predicate_scopes ⇒ [Scope, Scope]
- #self?.subclass_of? ⇒ Boolean
- #self?.trusted_equality_literal? ⇒ Boolean
- #self?.value_pattern_certainty ⇒ :yes, ...
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.
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/rigor/inference/narrowing.rb', line 408 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.
391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/rigor/inference/narrowing.rb', line 391 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 thewhenclause 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 nextwhenor theelse) 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.
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/rigor/inference/narrowing.rb', line 367 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.
135 136 137 138 139 140 141 142 143 |
# File 'lib/rigor/inference/narrowing.rb', line 135 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.
257 258 259 260 |
# File 'lib/rigor/inference/narrowing.rb', line 257 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.
168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/rigor/inference/narrowing.rb', line 168 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.
70 71 72 73 74 75 76 77 |
# File 'lib/rigor/inference/narrowing.rb', line 70 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.
318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/rigor/inference/narrowing.rb', line 318 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 toBot.IntegerRange[a..b]becomes the intersection with the half-line implied by the comparison; an empty intersection collapses toBot, a single-point intersection collapses toConstant<Integer>.Nominal[Integer]becomes the half-line itself (e.g.x > 0onNominal[Integer]ispositive_int).Unionnarrows 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.
209 210 211 212 213 |
# File 'lib/rigor/inference/narrowing.rb', line 209 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.
219 220 221 222 223 |
# File 'lib/rigor/inference/narrowing.rb', line 219 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.
230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/rigor/inference/narrowing.rb', line 230 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.
83 84 85 86 87 88 89 90 |
# File 'lib/rigor/inference/narrowing.rb', line 83 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?.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/rigor/inference/narrowing.rb', line 94 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.
266 267 268 269 |
# File 'lib/rigor/inference/narrowing.rb', line 266 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.
183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/rigor/inference/narrowing.rb', line 183 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 ofbase \ {v}withincurrent_type. Walk the current type's union members, keep each part disjoint frombase, and add the removed-value Constant once when any current member covers it.assert s is ~non-empty-stringovers: String | nilnarrows toConstant[""] | NilClass.IntegerRange[a, b](v0.0.5+ slice). Complement is the two open halvesint<min, a-1>andint<b+1, max>, each intersected with the integer-domain parts ofcurrent_type. Non-integer parts (nil, String, …) of a Union receiver survive unchanged.assert n is ~int<5, 10>overn: Integer | nilnarrows toint<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 withincurrent_typeand the results are unioned. Members the algebra cannot complement (Refined, non-Constant Difference, …) contributecurrent_typeitself, so the union widens the answer tocurrent_type— sound but imprecise.Refined[base, predicate]. Predicate complements are not reducible to a finite carrier without a richer shape (e.g.~lowercase-stringis "uppercase OR mixed-case");current_typeis returned unchanged.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/rigor/inference/narrowing.rb', line 294 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.
55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/rigor/inference/narrowing.rb', line 55 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.
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rigor/inference/narrowing.rb', line 117 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.
337 338 339 340 341 342 |
# File 'lib/rigor/inference/narrowing.rb', line 337 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).
155 156 157 158 159 160 |
# File 'lib/rigor/inference/narrowing.rb', line 155 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?.analyse ⇒ Object
117 |
# File 'sig/rigor/inference.rbs', line 117
def self?.analyse: (untyped node, Scope scope) -> untyped
|
#self?.case_when_scopes ⇒ [Scope, Scope]
116 |
# File 'sig/rigor/inference.rbs', line 116
def self?.case_when_scopes: (untyped subject, Array[untyped] conditions, Scope scope) -> [Scope, Scope]
|
#self?.class_ordering ⇒ Symbol
119 |
# File 'sig/rigor/inference.rbs', line 119
def self?.class_ordering: (String lhs, String rhs, untyped context) -> Symbol
|
#self?.class_pattern_certainty ⇒ :yes, ...
113 |
# File 'sig/rigor/inference.rbs', line 113
def self?.class_pattern_certainty: (Type::t subject_type, String class_name, environment: Environment) -> (:yes | :no | :maybe)
|
#self?.falsey_nominal? ⇒ Boolean
122 |
# File 'sig/rigor/inference.rbs', line 122
def self?.falsey_nominal?: (Type::Nominal nominal) -> bool
|
#self?.falsey_value? ⇒ Boolean
121 |
# File 'sig/rigor/inference.rbs', line 121
def self?.falsey_value?: (untyped value) -> bool
|
#self?.narrow_class ⇒ Type::t
108 |
# File 'sig/rigor/inference.rbs', line 108
def self?.narrow_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
|
#self?.narrow_equal ⇒ Type::t
106 |
# File 'sig/rigor/inference.rbs', line 106
def self?.narrow_equal: (Type::t type, untyped literal) -> Type::t
|
#self?.narrow_falsey ⇒ Type::t
103 |
# File 'sig/rigor/inference.rbs', line 103
def self?.narrow_falsey: (Type::t type) -> Type::t
|
#self?.narrow_for_fact ⇒ Type::t
111 |
# File 'sig/rigor/inference.rbs', line 111
def self?.narrow_for_fact: (Type::t current, untyped fact, untyped environment) -> Type::t
|
#self?.narrow_nil ⇒ Type::t
104 |
# File 'sig/rigor/inference.rbs', line 104
def self?.narrow_nil: (Type::t type) -> Type::t
|
#self?.narrow_non_nil ⇒ Type::t
105 |
# File 'sig/rigor/inference.rbs', line 105
def self?.narrow_non_nil: (Type::t type) -> Type::t
|
#self?.narrow_not_class ⇒ Type::t
109 |
# File 'sig/rigor/inference.rbs', line 109
def self?.narrow_not_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
|
#self?.narrow_not_equal ⇒ Type::t
107 |
# File 'sig/rigor/inference.rbs', line 107
def self?.narrow_not_equal: (Type::t type, untyped literal) -> Type::t
|
#self?.narrow_not_refinement ⇒ Type::t
110 |
# File 'sig/rigor/inference.rbs', line 110
def self?.narrow_not_refinement: (Type::t current_type, Type::t refinement_type) -> Type::t
|
#self?.narrow_truthy ⇒ Type::t
102 |
# File 'sig/rigor/inference.rbs', line 102
def self?.narrow_truthy: (Type::t type) -> Type::t
|
#self?.predicate_certainty ⇒ :truthy, ...
112 |
# File 'sig/rigor/inference.rbs', line 112
def self?.predicate_certainty: (Type::t? type) -> (:truthy | :falsey | nil)
|
#self?.predicate_scopes ⇒ [Scope, Scope]
115 |
# File 'sig/rigor/inference.rbs', line 115
def self?.predicate_scopes: (untyped node, Scope scope) -> [Scope, Scope]
|
#self?.subclass_of? ⇒ Boolean
118 |
# File 'sig/rigor/inference.rbs', line 118
def self?.subclass_of?: (String rigor_class_name, String target_class_name, untyped context) -> bool
|
#self?.trusted_equality_literal? ⇒ Boolean
120 |
# File 'sig/rigor/inference.rbs', line 120
def self?.trusted_equality_literal?: (untyped literal) -> bool
|
#self?.value_pattern_certainty ⇒ :yes, ...
114 |
# File 'sig/rigor/inference.rbs', line 114
def self?.value_pattern_certainty: (Type::t subject_type, untyped pattern_value) -> (:yes | :no | :maybe)
|