rigortype 0.1.19 → 0.2.0

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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -23
  3. data/lib/rigor/analysis/check_rules/rule_walk.rb +3 -21
  4. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +24 -15
  5. data/lib/rigor/analysis/check_rules.rb +492 -71
  6. data/lib/rigor/analysis/dependency_source_inference/index.rb +4 -7
  7. data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -18
  8. data/lib/rigor/analysis/dependency_source_inference.rb +3 -12
  9. data/lib/rigor/analysis/fact_store.rb +5 -4
  10. data/lib/rigor/analysis/rule_catalog.rb +153 -6
  11. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +17 -17
  12. data/lib/rigor/analysis/runner/project_pre_passes.rb +9 -8
  13. data/lib/rigor/analysis/runner.rb +17 -6
  14. data/lib/rigor/analysis/self_call_resolution_recorder.rb +3 -4
  15. data/lib/rigor/analysis/worker_session.rb +10 -14
  16. data/lib/rigor/builtins/predefined_constant_refinements.rb +151 -0
  17. data/lib/rigor/cache/store.rb +5 -3
  18. data/lib/rigor/cli/annotate_command.rb +28 -7
  19. data/lib/rigor/cli/baseline_command.rb +4 -3
  20. data/lib/rigor/cli/check_command.rb +115 -16
  21. data/lib/rigor/cli/coverage_command.rb +148 -16
  22. data/lib/rigor/cli/coverage_scan.rb +57 -0
  23. data/lib/rigor/cli/explain_command.rb +2 -0
  24. data/lib/rigor/cli/lsp_command.rb +3 -7
  25. data/lib/rigor/cli/mutation_protection_renderer.rb +63 -0
  26. data/lib/rigor/cli/mutation_protection_report.rb +73 -0
  27. data/lib/rigor/cli/options.rb +9 -0
  28. data/lib/rigor/cli/plugins_command.rb +2 -1
  29. data/lib/rigor/cli/protection_renderer.rb +63 -0
  30. data/lib/rigor/cli/protection_report.rb +68 -0
  31. data/lib/rigor/cli/sig_gen_command.rb +2 -1
  32. data/lib/rigor/cli/trace_command.rb +2 -1
  33. data/lib/rigor/cli/triage_command.rb +2 -1
  34. data/lib/rigor/cli/type_of_command.rb +1 -1
  35. data/lib/rigor/cli/type_scan_command.rb +2 -1
  36. data/lib/rigor/cli.rb +3 -2
  37. data/lib/rigor/configuration/dependencies.rb +2 -4
  38. data/lib/rigor/configuration.rb +45 -7
  39. data/lib/rigor/environment/bundle_sig_discovery.rb +61 -13
  40. data/lib/rigor/environment/class_registry.rb +4 -3
  41. data/lib/rigor/environment/constant_type_cache_holder.rb +43 -0
  42. data/lib/rigor/environment/lockfile_resolver.rb +1 -1
  43. data/lib/rigor/environment/rbs_collection_discovery.rb +1 -2
  44. data/lib/rigor/environment/rbs_coverage_report.rb +2 -1
  45. data/lib/rigor/environment/rbs_loader.rb +49 -5
  46. data/lib/rigor/environment.rb +17 -7
  47. data/lib/rigor/flow_contribution/fact.rb +1 -1
  48. data/lib/rigor/flow_contribution.rb +3 -5
  49. data/lib/rigor/inference/acceptance.rb +17 -9
  50. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  51. data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -2
  52. data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -2
  53. data/lib/rigor/inference/builtins/method_catalog.rb +19 -0
  54. data/lib/rigor/inference/builtins/string_catalog.rb +9 -1
  55. data/lib/rigor/inference/expression_typer.rb +20 -28
  56. data/lib/rigor/inference/hkt_body.rb +8 -11
  57. data/lib/rigor/inference/hkt_body_parser.rb +10 -12
  58. data/lib/rigor/inference/hkt_registry.rb +10 -11
  59. data/lib/rigor/inference/method_dispatcher/call_context.rb +1 -4
  60. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +156 -21
  61. data/lib/rigor/inference/method_dispatcher/data_folding.rb +9 -73
  62. data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -7
  63. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +10 -16
  64. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +25 -13
  65. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +93 -0
  66. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -3
  67. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +24 -22
  68. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +90 -15
  69. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +303 -0
  70. data/lib/rigor/inference/method_dispatcher.rb +40 -48
  71. data/lib/rigor/inference/mutation_widening.rb +5 -11
  72. data/lib/rigor/inference/narrowing.rb +14 -16
  73. data/lib/rigor/inference/parameter_inference_collector.rb +367 -0
  74. data/lib/rigor/inference/project_patched_methods.rb +4 -7
  75. data/lib/rigor/inference/project_patched_scanner.rb +2 -13
  76. data/lib/rigor/inference/protection_scanner.rb +86 -0
  77. data/lib/rigor/inference/scope_indexer.rb +129 -55
  78. data/lib/rigor/inference/statement_evaluator.rb +244 -114
  79. data/lib/rigor/inference/struct_fold_safety.rb +181 -0
  80. data/lib/rigor/inference/synthetic_method.rb +7 -7
  81. data/lib/rigor/language_server/completion_provider.rb +6 -12
  82. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  83. data/lib/rigor/language_server/document_symbol_provider.rb +3 -3
  84. data/lib/rigor/language_server/hover_provider.rb +2 -3
  85. data/lib/rigor/language_server/hover_renderer.rb +2 -11
  86. data/lib/rigor/language_server/server.rb +9 -17
  87. data/lib/rigor/language_server.rb +4 -5
  88. data/lib/rigor/plugin/base.rb +10 -8
  89. data/lib/rigor/plugin/macro/block_as_method.rb +3 -4
  90. data/lib/rigor/plugin/macro/heredoc_template.rb +4 -7
  91. data/lib/rigor/plugin/macro/trait_registry.rb +3 -6
  92. data/lib/rigor/plugin/macro.rb +4 -5
  93. data/lib/rigor/plugin/manifest.rb +45 -66
  94. data/lib/rigor/plugin/registry.rb +6 -7
  95. data/lib/rigor/plugin/type_node_resolver.rb +6 -8
  96. data/lib/rigor/protection/mutation_scanner.rb +120 -0
  97. data/lib/rigor/protection/mutator.rb +246 -0
  98. data/lib/rigor/rbs_extended.rb +24 -36
  99. data/lib/rigor/reflection.rb +4 -7
  100. data/lib/rigor/scope/discovery_index.rb +14 -2
  101. data/lib/rigor/scope.rb +54 -11
  102. data/lib/rigor/sig_gen/observed_call.rb +3 -3
  103. data/lib/rigor/sig_gen/writer.rb +40 -2
  104. data/lib/rigor/source/constant_path.rb +62 -0
  105. data/lib/rigor/source.rb +1 -0
  106. data/lib/rigor/type/bound_method.rb +2 -11
  107. data/lib/rigor/type/combinator.rb +16 -3
  108. data/lib/rigor/type/constant.rb +2 -11
  109. data/lib/rigor/type/data_class.rb +2 -11
  110. data/lib/rigor/type/data_instance.rb +2 -11
  111. data/lib/rigor/type/hash_shape.rb +2 -11
  112. data/lib/rigor/type/integer_range.rb +2 -11
  113. data/lib/rigor/type/intersection.rb +2 -11
  114. data/lib/rigor/type/nominal.rb +2 -11
  115. data/lib/rigor/type/plain_lattice.rb +37 -0
  116. data/lib/rigor/type/refined.rb +72 -13
  117. data/lib/rigor/type/singleton.rb +2 -11
  118. data/lib/rigor/type/struct_class.rb +75 -0
  119. data/lib/rigor/type/struct_instance.rb +93 -0
  120. data/lib/rigor/type/tuple.rb +5 -15
  121. data/lib/rigor/type.rb +2 -0
  122. data/lib/rigor/version.rb +1 -1
  123. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
  124. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +3 -3
  125. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +3 -3
  126. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +5 -13
  127. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +11 -17
  128. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +7 -10
  129. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +3 -2
  130. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
  131. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +6 -8
  132. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +5 -7
  133. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -2
  134. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +9 -11
  135. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +8 -9
  136. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +13 -12
  137. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +3 -4
  138. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +8 -8
  139. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +9 -11
  140. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +7 -8
  141. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +7 -9
  142. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +12 -13
  143. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +15 -23
  144. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +3 -3
  145. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +3 -3
  146. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +2 -4
  147. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +27 -11
  148. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -1
  149. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -6
  150. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +12 -18
  151. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +5 -5
  152. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -4
  153. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -1
  154. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
  155. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +19 -14
  156. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +0 -1
  157. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +5 -4
  158. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +2 -3
  159. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +7 -11
  160. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +4 -5
  161. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +6 -9
  162. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +5 -15
  163. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +28 -41
  164. data/sig/rigor/scope.rbs +9 -1
  165. data/sig/rigor/type.rbs +36 -1
  166. metadata +19 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e2fede9ea7407238b1f9c8f5784cc43f22724ee86decdf81f4a148c4ddff763
4
- data.tar.gz: 57e9757b838f29185d9b936ddff586d372126540afe12cefb1e98dbc7e0a6109
3
+ metadata.gz: e5c87db4b1ce8d734a2c7f8d7f0b7af6a7d643612f011c08676a5bca123ae4cb
4
+ data.tar.gz: b9340fe30f78245443d61d82965e08f2833e4787b938886f8f42890981a88cff
5
5
  SHA512:
6
- metadata.gz: 33b889562fefed7516dec175b7d85cc9f725ebcc7d94df30b5a58a436322709517a02bda2a0de10d746004c8c2d198aaa673f12577ddfe3ffeb37405508e1d9c
7
- data.tar.gz: f5773b727c0fb718df1ab1163add87f0dd7e94913fefd0599e7465e5a40afbfa201b2b14a09eb524b2e98dc6e30bd84ab0424355802ded977386f5a706d84837
6
+ metadata.gz: 94cafd68574caa752869465bcbf8a8e646bf302f4c540f9eb3dee122b6d455c193a21dd2debbf2c481f1676fb4c83aca131a3ba73508fef65c3dd1f2a3debaaa
7
+ data.tar.gz: f762d58ad77bd3e819f346b6de84408769320d6025030737b3611ce918db4c5e7943e0371b9b0603c27c2be8b8d2064975ff1e2bb113b4e3af8443bb2723fd05
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "prism"
4
4
 
5
+ require_relative "../../source/constant_path"
6
+
5
7
  module Rigor
6
8
  module Analysis
7
9
  module CheckRules
@@ -80,7 +82,7 @@ module Rigor
80
82
 
81
83
  case node
82
84
  when Prism::ClassNode, Prism::ModuleNode
83
- name = qualified_name_for(node.constant_path)
85
+ name = Source::ConstantPath.qualified_name(node.constant_path)
84
86
  if name
85
87
  walk(node.body, qualified_prefix + [name]) if node.body
86
88
  return
@@ -119,28 +121,6 @@ module Rigor
119
121
  @accumulator[class_name][node.name] ||= []
120
122
  @accumulator[class_name][node.name] << { node: node, type: rvalue_type }
121
123
  end
122
-
123
- # Same shape resolution as `ScopeIndexer.qualified_name_for`
124
- # (single-segment ConstantReadNode and dotted
125
- # ConstantPathNode). Inlined here to keep the collector
126
- # self-contained — the rule lives outside the indexer's
127
- # private surface.
128
- def qualified_name_for(constant_path_node)
129
- case constant_path_node
130
- when Prism::ConstantReadNode then constant_path_node.name.to_s
131
- when Prism::ConstantPathNode then render_constant_path(constant_path_node)
132
- end
133
- end
134
-
135
- def render_constant_path(node)
136
- prefix =
137
- case node.parent
138
- when Prism::ConstantReadNode then "#{node.parent.name}::"
139
- when Prism::ConstantPathNode then "#{render_constant_path(node.parent)}::"
140
- else ""
141
- end
142
- "#{prefix}#{node.name}"
143
- end
144
124
  end
145
125
  end
146
126
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "prism"
4
4
 
5
+ require_relative "../../source/constant_path"
6
+
5
7
  module Rigor
6
8
  module Analysis
7
9
  module CheckRules
@@ -183,29 +185,9 @@ module Rigor
183
185
  def extend_prefix(node, prefix)
184
186
  return prefix unless CLASS_OR_MODULE_NODE_CLASSES.any? { |klass| node.is_a?(klass) }
185
187
 
186
- name = qualified_name_for(node.constant_path)
188
+ name = Source::ConstantPath.qualified_name(node.constant_path)
187
189
  name ? prefix + [name] : prefix
188
190
  end
189
-
190
- # Same shape resolution as `IvarWriteCollector#qualified_name_for`
191
- # and `ScopeIndexer.qualified_name_for` (single-segment
192
- # ConstantReadNode and dotted ConstantPathNode).
193
- def qualified_name_for(constant_path_node)
194
- case constant_path_node
195
- when Prism::ConstantReadNode then constant_path_node.name.to_s
196
- when Prism::ConstantPathNode then render_constant_path(constant_path_node)
197
- end
198
- end
199
-
200
- def render_constant_path(node)
201
- prefix =
202
- case node.parent
203
- when Prism::ConstantReadNode then "#{node.parent.name}::"
204
- when Prism::ConstantPathNode then "#{render_constant_path(node.parent)}::"
205
- else ""
206
- end
207
- "#{prefix}#{node.name}"
208
- end
209
191
  end
210
192
  end
211
193
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "prism"
4
4
 
5
+ require_relative "../../source/constant_path"
6
+
5
7
  module Rigor
6
8
  module Analysis
7
9
  module CheckRules
@@ -45,20 +47,27 @@ module Rigor
45
47
 
46
48
  case node
47
49
  when Prism::ModuleNode
48
- name = constant_path_name(node.constant_path)
50
+ name = Source::ConstantPath.qualified_name_or_nil(node.constant_path)
49
51
  child_prefix = name ? prefix + [name] : prefix
50
52
  names << child_prefix.join("::") if name
51
53
  walk(node.body, child_prefix, names) if node.body
52
54
  when Prism::ClassNode
53
- name = constant_path_name(node.constant_path)
55
+ name = Source::ConstantPath.qualified_name_or_nil(node.constant_path)
54
56
  child_prefix = name ? prefix + [name] : prefix
55
- names << child_prefix.join("::") if name && dynamic_attr_class?(node)
57
+ names << child_prefix.join("::") if name && class_surface_open?(node)
56
58
  walk(node.body, child_prefix, names) if node.body
57
59
  else
58
60
  node.compact_child_nodes.each { |child| walk(child, prefix, names) }
59
61
  end
60
62
  end
61
63
 
64
+ # A class whose source-declared surface cannot be fully enumerated:
65
+ # a dynamic `attr_*` accessor, or a non-constant (dynamically produced)
66
+ # superclass.
67
+ def class_surface_open?(class_node)
68
+ dynamic_attr_class?(class_node) || dynamic_superclass?(class_node)
69
+ end
70
+
62
71
  # True when the class body directly invokes an `attr_*` macro with a
63
72
  # non-literal argument (a splat, a constant, a method call) — the
64
73
  # synthesized accessor names are then not statically knowable.
@@ -81,18 +90,18 @@ module Rigor
81
90
  args.any? { |arg| !arg.is_a?(Prism::SymbolNode) && !arg.is_a?(Prism::StringNode) }
82
91
  end
83
92
 
84
- # Renders a class/module path node to its source-qualified name
85
- # (`Foo`, `Foo::Bar`, leading `::` stripped); nil for a dynamic
86
- # constant path the scanner cannot name statically.
87
- def constant_path_name(node)
88
- case node
89
- when Prism::ConstantReadNode
90
- node.name.to_s
91
- when Prism::ConstantPathNode
92
- parent = node.parent ? constant_path_name(node.parent) : nil
93
- child = node.name.to_s
94
- node.parent.nil? ? child : (parent && "#{parent}::#{child}")
95
- end
93
+ # True when the class declares a superclass that is not a static
94
+ # constant `class X < DelegateClass(Array)` / `< Struct.new(...)` /
95
+ # `< Data.define(...)`. The inherited surface is then a dynamically
96
+ # produced class the engine cannot enumerate from a constant name (the
97
+ # superclass is a method call, so `discovered_superclasses` never
98
+ # records it and the closed-class gate would wrongly treat the class as
99
+ # standalone), so a missed self-call is not provably a typo.
100
+ def dynamic_superclass?(class_node)
101
+ superclass = class_node.superclass
102
+ return false if superclass.nil?
103
+
104
+ !superclass.is_a?(Prism::ConstantReadNode) && !superclass.is_a?(Prism::ConstantPathNode)
96
105
  end
97
106
  end
98
107
  end