rigortype 0.0.9 → 0.1.1
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/README.md +45 -2
- data/data/builtins/ruby_core/array.yml +6 -6
- data/data/builtins/ruby_core/hash.yml +1 -1
- data/data/builtins/ruby_core/io.yml +3 -3
- data/data/builtins/ruby_core/numeric.yml +1 -1
- data/data/builtins/ruby_core/pathname.yml +100 -100
- data/data/builtins/ruby_core/proc.yml +1 -1
- data/data/builtins/ruby_core/time.yml +3 -3
- data/lib/rigor/analysis/check_rules.rb +228 -40
- data/lib/rigor/analysis/diagnostic.rb +15 -1
- data/lib/rigor/analysis/runner.rb +269 -7
- data/lib/rigor/builtins/regex_refinement.rb +104 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +1 -1
- data/lib/rigor/cache/rbs_constant_table.rb +2 -2
- data/lib/rigor/cache/rbs_descriptor.rb +2 -0
- data/lib/rigor/cache/rbs_instance_definitions.rb +79 -0
- data/lib/rigor/cache/store.rb +2 -0
- data/lib/rigor/cli/type_of_command.rb +3 -3
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli.rb +20 -7
- data/lib/rigor/configuration/severity_profile.rb +109 -0
- data/lib/rigor/configuration.rb +286 -15
- data/lib/rigor/environment/rbs_loader.rb +89 -13
- data/lib/rigor/environment.rb +12 -4
- data/lib/rigor/flow_contribution/conflict.rb +81 -0
- data/lib/rigor/flow_contribution/element.rb +53 -0
- data/lib/rigor/flow_contribution/fact.rb +88 -0
- data/lib/rigor/flow_contribution/merge_result.rb +67 -0
- data/lib/rigor/flow_contribution/merger.rb +275 -0
- data/lib/rigor/flow_contribution.rb +51 -0
- data/lib/rigor/inference/block_parameter_binder.rb +15 -0
- data/lib/rigor/inference/expression_typer.rb +87 -6
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +31 -0
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +136 -9
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +21 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +68 -2
- data/lib/rigor/inference/method_dispatcher.rb +50 -1
- data/lib/rigor/inference/multi_target_binder.rb +2 -0
- data/lib/rigor/inference/narrowing.rb +246 -127
- data/lib/rigor/inference/scope_indexer.rb +124 -16
- data/lib/rigor/inference/statement_evaluator.rb +406 -37
- data/lib/rigor/plugin/access_denied_error.rb +24 -0
- data/lib/rigor/plugin/base.rb +284 -0
- data/lib/rigor/plugin/fact_store.rb +92 -0
- data/lib/rigor/plugin/io_boundary.rb +102 -0
- data/lib/rigor/plugin/load_error.rb +35 -0
- data/lib/rigor/plugin/loader.rb +307 -0
- data/lib/rigor/plugin/manifest.rb +203 -0
- data/lib/rigor/plugin/registry.rb +50 -0
- data/lib/rigor/plugin/services.rb +77 -0
- data/lib/rigor/plugin/trust_policy.rb +99 -0
- data/lib/rigor/plugin.rb +62 -0
- data/lib/rigor/rbs_extended.rb +57 -9
- data/lib/rigor/reflection.rb +2 -2
- data/lib/rigor/trinary.rb +1 -1
- data/lib/rigor/type/integer_range.rb +6 -2
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +7 -0
- data/sig/rigor/environment.rbs +10 -3
- data/sig/rigor/inference.rbs +1 -0
- data/sig/rigor/rbs_extended.rbs +2 -0
- data/sig/rigor/scope.rbs +1 -0
- data/sig/rigor/type.rbs +7 -0
- data/sig/rigor.rbs +8 -2
- metadata +20 -1
|
@@ -340,7 +340,7 @@ module Rigor
|
|
|
340
340
|
accumulator.transform_values(&:freeze).freeze
|
|
341
341
|
end
|
|
342
342
|
|
|
343
|
-
def walk_methods(node, qualified_prefix, in_singleton_class, accumulator) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
343
|
+
def walk_methods(node, qualified_prefix, in_singleton_class, accumulator) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
344
344
|
return unless node.is_a?(Prism::Node)
|
|
345
345
|
|
|
346
346
|
case node
|
|
@@ -359,6 +359,9 @@ module Rigor
|
|
|
359
359
|
when Prism::DefNode
|
|
360
360
|
record_def_method(node, qualified_prefix, in_singleton_class, accumulator)
|
|
361
361
|
return
|
|
362
|
+
when Prism::AliasMethodNode
|
|
363
|
+
record_alias_method(node, qualified_prefix, in_singleton_class, accumulator)
|
|
364
|
+
return
|
|
362
365
|
when Prism::CallNode
|
|
363
366
|
record_define_method(node, qualified_prefix, in_singleton_class, accumulator) if node.name == :define_method
|
|
364
367
|
end
|
|
@@ -390,6 +393,7 @@ module Rigor
|
|
|
390
393
|
def build_discovered_def_nodes(root)
|
|
391
394
|
accumulator = {}
|
|
392
395
|
walk_def_nodes(root, [], false, accumulator)
|
|
396
|
+
apply_alias_def_nodes(root, accumulator)
|
|
393
397
|
accumulator.transform_values(&:freeze).freeze
|
|
394
398
|
end
|
|
395
399
|
|
|
@@ -436,6 +440,76 @@ module Rigor
|
|
|
436
440
|
accumulator[class_name][def_node.name] = def_node
|
|
437
441
|
end
|
|
438
442
|
|
|
443
|
+
# Registers the alias name in the `discovered_methods` table so
|
|
444
|
+
# `undefined-method` diagnostics are not emitted for calls to the
|
|
445
|
+
# aliased name. The kind mirrors the surrounding class context
|
|
446
|
+
# (instance inside a regular class body, singleton inside
|
|
447
|
+
# `class << self`).
|
|
448
|
+
def record_alias_method(alias_node, qualified_prefix, in_singleton_class, accumulator)
|
|
449
|
+
return if qualified_prefix.empty?
|
|
450
|
+
return unless alias_node.new_name.is_a?(Prism::SymbolNode)
|
|
451
|
+
|
|
452
|
+
class_name = qualified_prefix.join("::")
|
|
453
|
+
new_name = alias_node.new_name.unescaped.to_sym
|
|
454
|
+
kind = in_singleton_class ? :singleton : :instance
|
|
455
|
+
(accumulator[class_name] ||= {})[new_name] = kind
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Post-pass over the `def_nodes` accumulator: for every `alias`
|
|
459
|
+
# declaration inside a class body, if the original method name
|
|
460
|
+
# maps to a `Prism::DefNode`, register the new name pointing to
|
|
461
|
+
# the same node so inter-procedural return-type inference works
|
|
462
|
+
# for the aliased name.
|
|
463
|
+
def apply_alias_def_nodes(root, accumulator)
|
|
464
|
+
alias_map = collect_class_alias_map(root, [], {})
|
|
465
|
+
alias_map.each do |class_name, aliases|
|
|
466
|
+
class_defs = accumulator[class_name]
|
|
467
|
+
next unless class_defs
|
|
468
|
+
|
|
469
|
+
aliases.each do |new_name, old_name|
|
|
470
|
+
def_node = class_defs[old_name]
|
|
471
|
+
next unless def_node.is_a?(Prism::DefNode)
|
|
472
|
+
|
|
473
|
+
(accumulator[class_name] ||= {})[new_name] = def_node
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Builds a map `{class_name => {new_name_sym => old_name_sym}}` by
|
|
479
|
+
# walking the tree for `AliasMethodNode` nodes inside class bodies.
|
|
480
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
481
|
+
def collect_class_alias_map(node, qualified_prefix, accumulator)
|
|
482
|
+
return accumulator unless node.is_a?(Prism::Node)
|
|
483
|
+
|
|
484
|
+
case node
|
|
485
|
+
when Prism::ClassNode, Prism::ModuleNode
|
|
486
|
+
name = qualified_name_for(node.constant_path)
|
|
487
|
+
if name
|
|
488
|
+
collect_class_alias_map(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
489
|
+
return accumulator
|
|
490
|
+
end
|
|
491
|
+
when Prism::SingletonClassNode
|
|
492
|
+
return accumulator
|
|
493
|
+
when Prism::AliasMethodNode
|
|
494
|
+
record_alias_map_entry(node, qualified_prefix, accumulator)
|
|
495
|
+
return accumulator
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
node.compact_child_nodes.each { |child| collect_class_alias_map(child, qualified_prefix, accumulator) }
|
|
499
|
+
accumulator
|
|
500
|
+
end
|
|
501
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
502
|
+
|
|
503
|
+
def record_alias_map_entry(alias_node, qualified_prefix, accumulator)
|
|
504
|
+
return if qualified_prefix.empty?
|
|
505
|
+
return unless alias_node.new_name.is_a?(Prism::SymbolNode) && alias_node.old_name.is_a?(Prism::SymbolNode)
|
|
506
|
+
|
|
507
|
+
class_name = qualified_prefix.join("::")
|
|
508
|
+
new_name = alias_node.new_name.unescaped.to_sym
|
|
509
|
+
old_name = alias_node.old_name.unescaped.to_sym
|
|
510
|
+
(accumulator[class_name] ||= {})[new_name] = old_name
|
|
511
|
+
end
|
|
512
|
+
|
|
439
513
|
def record_define_method(call_node, qualified_prefix, in_singleton_class, accumulator)
|
|
440
514
|
return if qualified_prefix.empty?
|
|
441
515
|
return if call_node.arguments.nil? || call_node.arguments.arguments.empty?
|
|
@@ -478,7 +552,7 @@ module Rigor
|
|
|
478
552
|
when Prism::ModuleNode, Prism::ClassNode
|
|
479
553
|
return if record_class_or_module?(node, qualified_prefix, identity_table, discovered)
|
|
480
554
|
when Prism::ConstantWriteNode
|
|
481
|
-
return if
|
|
555
|
+
return if record_meta_new_constant?(node, qualified_prefix, identity_table, discovered)
|
|
482
556
|
end
|
|
483
557
|
|
|
484
558
|
node.compact_child_nodes.each do |child|
|
|
@@ -499,17 +573,23 @@ module Rigor
|
|
|
499
573
|
true
|
|
500
574
|
end
|
|
501
575
|
|
|
502
|
-
# Recognises
|
|
503
|
-
# `Const` (qualified by the surrounding
|
|
504
|
-
# discovered class. `Const.new(...)`
|
|
505
|
-
# `Nominal[Const]` via `meta_new`,
|
|
506
|
-
# `Dynamic[top]` returned by the
|
|
576
|
+
# Recognises class-creating meta calls at constant-write rvalue
|
|
577
|
+
# position and registers `Const` (qualified by the surrounding
|
|
578
|
+
# class/module path) as a discovered class. `Const.new(...)`
|
|
579
|
+
# then resolves to a fresh `Nominal[Const]` via `meta_new`,
|
|
580
|
+
# instead of the un-narrowed `Dynamic[top]` returned by the
|
|
581
|
+
# default `Class#new` envelope.
|
|
507
582
|
#
|
|
508
|
-
#
|
|
509
|
-
#
|
|
510
|
-
#
|
|
511
|
-
|
|
512
|
-
|
|
583
|
+
# Two recognised meta forms:
|
|
584
|
+
#
|
|
585
|
+
# - `Const = Data.define(*Symbol) [do ... end]`
|
|
586
|
+
# - `Const = Struct.new(*Symbol [, keyword_init: ...]) [do ... end]`
|
|
587
|
+
#
|
|
588
|
+
# The block body, if present, is recursed into so any nested
|
|
589
|
+
# class/module declarations in the override block (rare but legal)
|
|
590
|
+
# still feed the discovered table.
|
|
591
|
+
def record_meta_new_constant?(node, qualified_prefix, identity_table, discovered)
|
|
592
|
+
return false unless data_define_call?(node.value) || struct_new_call?(node.value)
|
|
513
593
|
|
|
514
594
|
full = (qualified_prefix + [node.name.to_s]).join("::")
|
|
515
595
|
discovered[full] = Type::Combinator.singleton_of(full)
|
|
@@ -525,18 +605,46 @@ module Rigor
|
|
|
525
605
|
def data_define_call?(node)
|
|
526
606
|
return false unless node.is_a?(Prism::CallNode)
|
|
527
607
|
return false unless node.name == :define
|
|
528
|
-
return false unless
|
|
608
|
+
return false unless meta_constant_receiver?(node.receiver, :Data)
|
|
529
609
|
|
|
530
610
|
args = node.arguments&.arguments || []
|
|
531
611
|
args.all?(Prism::SymbolNode)
|
|
532
612
|
end
|
|
533
613
|
|
|
534
|
-
|
|
614
|
+
# Recognises `Struct.new(*Symbol)` and
|
|
615
|
+
# `Struct.new(*Symbol, keyword_init: <expr>)` at constant-write
|
|
616
|
+
# rvalue position. A trailing `KeywordHashNode` (the
|
|
617
|
+
# `keyword_init: ...` form) is accepted but does not contribute
|
|
618
|
+
# to member discovery; every other argument MUST be a
|
|
619
|
+
# `Prism::SymbolNode`. At least one Symbol member is required —
|
|
620
|
+
# `Struct.new()` is a degenerate form callers don't typically use.
|
|
621
|
+
def struct_new_call?(node)
|
|
622
|
+
return false unless meta_call_with_name?(node, :Struct, :new)
|
|
623
|
+
|
|
624
|
+
args = node.arguments&.arguments || []
|
|
625
|
+
positional = struct_new_positionals(args)
|
|
626
|
+
return false if positional.nil? || positional.empty?
|
|
627
|
+
|
|
628
|
+
positional.all?(Prism::SymbolNode)
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def meta_call_with_name?(node, receiver_name, method_name)
|
|
632
|
+
return false unless node.is_a?(Prism::CallNode)
|
|
633
|
+
return false unless node.name == method_name
|
|
634
|
+
|
|
635
|
+
meta_constant_receiver?(node.receiver, receiver_name)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def struct_new_positionals(args)
|
|
639
|
+
args.last.is_a?(Prism::KeywordHashNode) ? args[0..-2] : args
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def meta_constant_receiver?(node, expected_name)
|
|
535
643
|
case node
|
|
536
644
|
when Prism::ConstantReadNode
|
|
537
|
-
node.name ==
|
|
645
|
+
node.name == expected_name
|
|
538
646
|
when Prism::ConstantPathNode
|
|
539
|
-
node.parent.nil? && node.name ==
|
|
647
|
+
node.parent.nil? && node.name == expected_name
|
|
540
648
|
end
|
|
541
649
|
end
|
|
542
650
|
|