rigortype 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/builtins/ruby_core/array.yml +1470 -0
- data/data/builtins/ruby_core/file.yml +501 -0
- data/data/builtins/ruby_core/io.yml +1594 -0
- data/data/builtins/ruby_core/numeric.yml +1809 -0
- data/data/builtins/ruby_core/string.yml +1850 -0
- data/lib/rigor/analysis/check_rules.rb +297 -5
- data/lib/rigor/analysis/diagnostic.rb +13 -2
- data/lib/rigor/analysis/runner.rb +52 -5
- data/lib/rigor/builtins/imported_refinements.rb +69 -0
- data/lib/rigor/cli/type_of_command.rb +11 -5
- data/lib/rigor/cli/type_scan_command.rb +13 -8
- data/lib/rigor/cli.rb +26 -6
- data/lib/rigor/configuration.rb +18 -2
- data/lib/rigor/environment.rb +3 -1
- data/lib/rigor/inference/acceptance.rb +180 -0
- data/lib/rigor/inference/builtins/array_catalog.rb +46 -0
- data/lib/rigor/inference/builtins/method_catalog.rb +90 -0
- data/lib/rigor/inference/builtins/numeric_catalog.rb +93 -0
- data/lib/rigor/inference/builtins/string_catalog.rb +39 -0
- data/lib/rigor/inference/expression_typer.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +650 -16
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +144 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +113 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +107 -0
- data/lib/rigor/inference/method_dispatcher.rb +28 -21
- data/lib/rigor/inference/narrowing.rb +471 -10
- data/lib/rigor/inference/scope_indexer.rb +66 -0
- data/lib/rigor/inference/statement_evaluator.rb +305 -2
- data/lib/rigor/rbs_extended.rb +174 -14
- data/lib/rigor/scope.rb +44 -5
- data/lib/rigor/type/combinator.rb +69 -1
- data/lib/rigor/type/difference.rb +155 -0
- data/lib/rigor/type/integer_range.rb +137 -0
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/inference.rbs +5 -2
- data/sig/rigor/rbs_extended.rbs +25 -1
- data/sig/rigor/scope.rbs +4 -0
- data/sig/rigor/type.rbs +51 -1
- metadata +15 -1
|
@@ -744,6 +744,33 @@ module Rigor
|
|
|
744
744
|
arg_types = call_arg_types(node)
|
|
745
745
|
block_type = block_return_type_for(node, receiver, arg_types)
|
|
746
746
|
|
|
747
|
+
# v0.0.3 A — implicit-self calls prefer a same-named
|
|
748
|
+
# top-level `def` over RBS dispatch. Without this,
|
|
749
|
+
# a helper like `def select(...)` defined inside an
|
|
750
|
+
# `RSpec.describe ... do ... end` block mis-routes
|
|
751
|
+
# through `Enumerable#select` / `Object#select` and
|
|
752
|
+
# the caller observes `Array[Elem]` instead of the
|
|
753
|
+
# helper's actual return type. The check fires only
|
|
754
|
+
# for `node.receiver.nil?` (true implicit self), so
|
|
755
|
+
# explicit-receiver dispatch is unaffected.
|
|
756
|
+
local_def = node.receiver.nil? ? scope.top_level_def_for(node.name) : nil
|
|
757
|
+
if local_def
|
|
758
|
+
local_inference = infer_top_level_user_method(local_def, receiver, arg_types)
|
|
759
|
+
return local_inference if local_inference
|
|
760
|
+
|
|
761
|
+
# The local def matches by name but the
|
|
762
|
+
# parameter shape is too complex for the first-
|
|
763
|
+
# iteration binder (kwargs / optionals / rest).
|
|
764
|
+
# Returning `Dynamic[Top]` is the safest answer:
|
|
765
|
+
# we know RBS dispatch would be wrong (the
|
|
766
|
+
# method is user-defined and shadows whatever
|
|
767
|
+
# ancestor method the dispatch would find), and
|
|
768
|
+
# `Dynamic[Top]` propagates correctly through
|
|
769
|
+
# downstream call chains without surfacing
|
|
770
|
+
# misleading false-positive diagnostics.
|
|
771
|
+
return dynamic_top
|
|
772
|
+
end
|
|
773
|
+
|
|
747
774
|
result = MethodDispatcher.dispatch(
|
|
748
775
|
receiver_type: receiver,
|
|
749
776
|
method_name: node.name,
|
|
@@ -753,6 +780,14 @@ module Rigor
|
|
|
753
780
|
)
|
|
754
781
|
return result if result
|
|
755
782
|
|
|
783
|
+
# v0.0.2 #5 — inter-procedural inference for
|
|
784
|
+
# user-defined methods. When dispatch misses but the
|
|
785
|
+
# receiver is a user class with a `def` body, re-type
|
|
786
|
+
# the body with the call's argument types bound and
|
|
787
|
+
# return the body's last-expression type.
|
|
788
|
+
user_inference = try_user_method_inference(receiver, node, arg_types)
|
|
789
|
+
return user_inference if user_inference
|
|
790
|
+
|
|
756
791
|
# Dynamic-origin propagation: when the receiver is Dynamic[T] and
|
|
757
792
|
# no positive rule resolves the call, the result inherits the
|
|
758
793
|
# dynamic origin. Per the value-lattice algebra, this is a
|
|
@@ -763,6 +798,122 @@ module Rigor
|
|
|
763
798
|
fallback_for(node, family: :prism)
|
|
764
799
|
end
|
|
765
800
|
|
|
801
|
+
# v0.0.2 #5 — re-types the body of a user-defined
|
|
802
|
+
# instance method with the call site's argument types
|
|
803
|
+
# bound to the method's parameters. Used as a
|
|
804
|
+
# last-resort tier after `MethodDispatcher.dispatch`
|
|
805
|
+
# has exhausted its catalogue (RBS, shape, constant
|
|
806
|
+
# folding, user-class fallback). Returns nil when:
|
|
807
|
+
#
|
|
808
|
+
# - the receiver is not `Nominal[T]` for some T;
|
|
809
|
+
# - no def_node is recorded for that class/method
|
|
810
|
+
# (the receiver is foreign or has only an RBS sig);
|
|
811
|
+
# - the def has no body, or has a parameter shape we
|
|
812
|
+
# cannot bind from the call's positional args;
|
|
813
|
+
# - the inference is already in progress for this
|
|
814
|
+
# (class, method, signature) tuple — recursion
|
|
815
|
+
# safety net.
|
|
816
|
+
# v0.0.3 A — re-types a top-level (or DSL-block-nested)
|
|
817
|
+
# `def` discovered by `ScopeIndexer` under the
|
|
818
|
+
# `TOP_LEVEL_DEF_KEY` sentinel. Mirrors the
|
|
819
|
+
# `infer_user_method_return` shape but uses the
|
|
820
|
+
# current `scope.self_type` (or implicit `Object`)
|
|
821
|
+
# as the receiver carrier so the body's own self is
|
|
822
|
+
# consistent with the call site's. Returns nil when
|
|
823
|
+
# the parameter shape disqualifies the def, when the
|
|
824
|
+
# body is empty, or when a recursion cycle is
|
|
825
|
+
# detected.
|
|
826
|
+
def infer_top_level_user_method(def_node, receiver, arg_types)
|
|
827
|
+
infer_user_method_return(def_node, receiver, arg_types)
|
|
828
|
+
rescue StandardError
|
|
829
|
+
nil
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
def try_user_method_inference(receiver, call_node, arg_types)
|
|
833
|
+
return nil unless receiver.is_a?(Type::Nominal)
|
|
834
|
+
|
|
835
|
+
def_node = scope.user_def_for(receiver.class_name, call_node.name)
|
|
836
|
+
return nil if def_node.nil?
|
|
837
|
+
|
|
838
|
+
infer_user_method_return(def_node, receiver, arg_types)
|
|
839
|
+
rescue StandardError
|
|
840
|
+
nil
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
INFERENCE_GUARD_KEY = :__rigor_user_method_inference_stack__
|
|
844
|
+
private_constant :INFERENCE_GUARD_KEY
|
|
845
|
+
|
|
846
|
+
def infer_user_method_return(def_node, receiver, arg_types)
|
|
847
|
+
return nil if def_node.body.nil?
|
|
848
|
+
|
|
849
|
+
body_scope = build_user_method_body_scope(def_node, receiver, arg_types)
|
|
850
|
+
return nil if body_scope.nil?
|
|
851
|
+
|
|
852
|
+
# Recursion-guard signature. Uses `describe(:short)`
|
|
853
|
+
# so non-Nominal receivers (e.g. the implicit
|
|
854
|
+
# `Object` carrier used for top-level / DSL-block
|
|
855
|
+
# defs in v0.0.3 A) can participate without raising.
|
|
856
|
+
signature = [receiver.describe(:short), def_node.name, arg_types.map { |t| t.describe(:short) }]
|
|
857
|
+
stack = (Thread.current[INFERENCE_GUARD_KEY] ||= [])
|
|
858
|
+
return Type::Combinator.untyped if stack.include?(signature)
|
|
859
|
+
|
|
860
|
+
stack.push(signature)
|
|
861
|
+
begin
|
|
862
|
+
type, _post = body_scope.evaluate(def_node.body)
|
|
863
|
+
type
|
|
864
|
+
ensure
|
|
865
|
+
stack.pop
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
# Builds the body scope for a user-defined instance
|
|
870
|
+
# method call: a fresh `Scope` with `self_type` set to
|
|
871
|
+
# the receiver's nominal type, the project-wide
|
|
872
|
+
# accumulators inherited (so the body sees the same
|
|
873
|
+
# `discovered_classes` / `class_ivars` / etc. the
|
|
874
|
+
# caller does), and required positional parameters
|
|
875
|
+
# bound from the call's `arg_types` by index. Returns
|
|
876
|
+
# nil when the parameter shape is too complex for the
|
|
877
|
+
# first-iteration binder (rest args, keyword args,
|
|
878
|
+
# block params, etc.).
|
|
879
|
+
def build_user_method_body_scope(def_node, receiver, arg_types) # rubocop:disable Metrics/AbcSize
|
|
880
|
+
params = def_node.parameters
|
|
881
|
+
required = params&.requireds || []
|
|
882
|
+
return nil unless params.nil? || user_method_param_shape_simple?(params)
|
|
883
|
+
return nil unless required.size == arg_types.size
|
|
884
|
+
|
|
885
|
+
fresh = Scope.empty(environment: scope.environment)
|
|
886
|
+
.with_declared_types(scope.declared_types)
|
|
887
|
+
.with_discovered_classes(scope.discovered_classes)
|
|
888
|
+
.with_in_source_constants(scope.in_source_constants)
|
|
889
|
+
.with_class_ivars(scope.class_ivars)
|
|
890
|
+
.with_class_cvars(scope.class_cvars)
|
|
891
|
+
.with_program_globals(scope.program_globals)
|
|
892
|
+
.with_discovered_methods(scope.discovered_methods)
|
|
893
|
+
.with_discovered_def_nodes(scope.discovered_def_nodes)
|
|
894
|
+
.with_self_type(receiver)
|
|
895
|
+
|
|
896
|
+
required.each_with_index do |param, index|
|
|
897
|
+
fresh = fresh.with_local(param.name, arg_types[index])
|
|
898
|
+
end
|
|
899
|
+
fresh
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# First iteration accepts only required positional
|
|
903
|
+
# parameters: `def foo(a, b, c)`. Optionals, rest,
|
|
904
|
+
# keyword params, and block params disqualify the
|
|
905
|
+
# method from inference (the caller observes
|
|
906
|
+
# `Dynamic[Top]` instead).
|
|
907
|
+
def user_method_param_shape_simple?(params)
|
|
908
|
+
return false unless params.is_a?(Prism::ParametersNode)
|
|
909
|
+
|
|
910
|
+
params.optionals.empty? &&
|
|
911
|
+
params.rest.nil? &&
|
|
912
|
+
params.keywords.empty? &&
|
|
913
|
+
params.keyword_rest.nil? &&
|
|
914
|
+
params.block.nil?
|
|
915
|
+
end
|
|
916
|
+
|
|
766
917
|
# Slice A-engine. Implicit-self calls (no `node.receiver`)
|
|
767
918
|
# adopt the surrounding scope's `self_type` as their receiver
|
|
768
919
|
# so calls like `attr_reader_method_name` or
|