rigortype 0.1.19 → 0.2.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.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -6
  3. data/data/core_overlay/numeric.rbs +33 -0
  4. data/data/core_overlay/pathname.rbs +25 -0
  5. data/data/core_overlay/string_scanner.rbs +28 -0
  6. data/data/gem_overlay/activesupport/core_ext.rbs +473 -0
  7. data/data/vendored_gem_sigs/ast/ast.rbs +130 -0
  8. data/data/vendored_gem_sigs/bcrypt/bcrypt.rbs +47 -0
  9. data/data/vendored_gem_sigs/bundler/bundler.rbs +238 -0
  10. data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +34 -0
  11. data/data/vendored_gem_sigs/did_you_mean/did_you_mean_extras.rbs +34 -0
  12. data/data/vendored_gem_sigs/idn-ruby/idn.rbs +54 -0
  13. data/data/vendored_gem_sigs/mysql2/client.rbs +55 -0
  14. data/data/vendored_gem_sigs/mysql2/error.rbs +5 -0
  15. data/data/vendored_gem_sigs/mysql2/result.rbs +31 -0
  16. data/data/vendored_gem_sigs/mysql2/statement.rbs +5 -0
  17. data/data/vendored_gem_sigs/nokogiri/nokogiri.rbs +2332 -0
  18. data/data/vendored_gem_sigs/nokogiri/nokogiri_html5.rbs +47 -0
  19. data/data/vendored_gem_sigs/pg/pg.rbs +212 -0
  20. data/data/vendored_gem_sigs/prism/prism_supplement.rbs +44 -0
  21. data/data/vendored_gem_sigs/redis/errors.rbs +50 -0
  22. data/data/vendored_gem_sigs/redis/future.rbs +5 -0
  23. data/data/vendored_gem_sigs/redis/redis.rbs +348 -0
  24. data/data/vendored_gem_sigs/redis/redis_extras.rbs +130 -0
  25. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +226 -0
  26. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -23
  27. data/lib/rigor/analysis/check_rules/rule_walk.rb +3 -21
  28. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +24 -15
  29. data/lib/rigor/analysis/check_rules.rb +492 -71
  30. data/lib/rigor/analysis/dependency_source_inference/index.rb +4 -7
  31. data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -18
  32. data/lib/rigor/analysis/dependency_source_inference.rb +3 -12
  33. data/lib/rigor/analysis/fact_store.rb +5 -4
  34. data/lib/rigor/analysis/rule_catalog.rb +153 -6
  35. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +17 -17
  36. data/lib/rigor/analysis/runner/project_pre_passes.rb +9 -8
  37. data/lib/rigor/analysis/runner.rb +17 -6
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +3 -4
  39. data/lib/rigor/analysis/worker_session.rb +10 -14
  40. data/lib/rigor/builtins/predefined_constant_refinements.rb +151 -0
  41. data/lib/rigor/cache/store.rb +5 -3
  42. data/lib/rigor/cli/annotate_command.rb +28 -7
  43. data/lib/rigor/cli/baseline_command.rb +4 -3
  44. data/lib/rigor/cli/check_command.rb +138 -16
  45. data/lib/rigor/cli/coverage_command.rb +138 -31
  46. data/lib/rigor/cli/coverage_mutation.rb +149 -0
  47. data/lib/rigor/cli/coverage_scan.rb +57 -0
  48. data/lib/rigor/cli/explain_command.rb +2 -0
  49. data/lib/rigor/cli/fused_protection_renderer.rb +67 -0
  50. data/lib/rigor/cli/fused_protection_report.rb +76 -0
  51. data/lib/rigor/cli/lsp_command.rb +3 -7
  52. data/lib/rigor/cli/mutation_protection_renderer.rb +63 -0
  53. data/lib/rigor/cli/mutation_protection_report.rb +73 -0
  54. data/lib/rigor/cli/options.rb +9 -0
  55. data/lib/rigor/cli/plugins_command.rb +2 -1
  56. data/lib/rigor/cli/protection_renderer.rb +63 -0
  57. data/lib/rigor/cli/protection_report.rb +68 -0
  58. data/lib/rigor/cli/sig_gen_command.rb +2 -1
  59. data/lib/rigor/cli/trace_command.rb +2 -1
  60. data/lib/rigor/cli/triage_command.rb +2 -1
  61. data/lib/rigor/cli/type_of_command.rb +1 -1
  62. data/lib/rigor/cli/type_scan_command.rb +2 -1
  63. data/lib/rigor/cli.rb +3 -2
  64. data/lib/rigor/config_audit.rb +152 -0
  65. data/lib/rigor/configuration/dependencies.rb +2 -4
  66. data/lib/rigor/configuration.rb +57 -7
  67. data/lib/rigor/environment/bundle_sig_discovery.rb +61 -13
  68. data/lib/rigor/environment/class_registry.rb +4 -3
  69. data/lib/rigor/environment/constant_type_cache_holder.rb +43 -0
  70. data/lib/rigor/environment/lockfile_resolver.rb +1 -1
  71. data/lib/rigor/environment/rbs_collection_discovery.rb +1 -2
  72. data/lib/rigor/environment/rbs_coverage_report.rb +2 -1
  73. data/lib/rigor/environment/rbs_loader.rb +76 -5
  74. data/lib/rigor/environment.rb +66 -8
  75. data/lib/rigor/flow_contribution/fact.rb +1 -1
  76. data/lib/rigor/flow_contribution.rb +3 -5
  77. data/lib/rigor/inference/acceptance.rb +17 -9
  78. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  79. data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -2
  80. data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -2
  81. data/lib/rigor/inference/builtins/method_catalog.rb +19 -0
  82. data/lib/rigor/inference/builtins/string_catalog.rb +9 -1
  83. data/lib/rigor/inference/expression_typer.rb +20 -28
  84. data/lib/rigor/inference/hkt_body.rb +8 -11
  85. data/lib/rigor/inference/hkt_body_parser.rb +10 -12
  86. data/lib/rigor/inference/hkt_registry.rb +10 -11
  87. data/lib/rigor/inference/method_dispatcher/call_context.rb +1 -4
  88. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +169 -24
  89. data/lib/rigor/inference/method_dispatcher/data_folding.rb +9 -73
  90. data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -7
  91. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +10 -16
  92. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +25 -13
  93. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +93 -0
  94. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -3
  95. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +24 -22
  96. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +90 -15
  97. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +303 -0
  98. data/lib/rigor/inference/method_dispatcher.rb +40 -48
  99. data/lib/rigor/inference/mutation_widening.rb +5 -11
  100. data/lib/rigor/inference/narrowing.rb +14 -16
  101. data/lib/rigor/inference/parameter_inference_collector.rb +367 -0
  102. data/lib/rigor/inference/project_patched_methods.rb +4 -7
  103. data/lib/rigor/inference/project_patched_scanner.rb +2 -13
  104. data/lib/rigor/inference/protection_scanner.rb +86 -0
  105. data/lib/rigor/inference/scope_indexer.rb +129 -55
  106. data/lib/rigor/inference/statement_evaluator.rb +271 -114
  107. data/lib/rigor/inference/struct_fold_safety.rb +181 -0
  108. data/lib/rigor/inference/synthetic_method.rb +7 -7
  109. data/lib/rigor/language_server/completion_provider.rb +6 -12
  110. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  111. data/lib/rigor/language_server/document_symbol_provider.rb +3 -3
  112. data/lib/rigor/language_server/hover_provider.rb +2 -3
  113. data/lib/rigor/language_server/hover_renderer.rb +2 -11
  114. data/lib/rigor/language_server/server.rb +9 -17
  115. data/lib/rigor/language_server.rb +4 -5
  116. data/lib/rigor/plugin/base.rb +10 -8
  117. data/lib/rigor/plugin/macro/block_as_method.rb +3 -4
  118. data/lib/rigor/plugin/macro/heredoc_template.rb +4 -7
  119. data/lib/rigor/plugin/macro/trait_registry.rb +3 -6
  120. data/lib/rigor/plugin/macro.rb +4 -5
  121. data/lib/rigor/plugin/manifest.rb +45 -66
  122. data/lib/rigor/plugin/registry.rb +6 -7
  123. data/lib/rigor/plugin/type_node_resolver.rb +6 -8
  124. data/lib/rigor/protection/diagnostic_oracle.rb +51 -0
  125. data/lib/rigor/protection/mutation_scanner.rb +180 -0
  126. data/lib/rigor/protection/mutator.rb +267 -0
  127. data/lib/rigor/protection/test_suite_oracle.rb +68 -0
  128. data/lib/rigor/rbs_extended.rb +24 -36
  129. data/lib/rigor/reflection.rb +4 -7
  130. data/lib/rigor/scope/discovery_index.rb +14 -2
  131. data/lib/rigor/scope.rb +54 -11
  132. data/lib/rigor/sig_gen/observed_call.rb +3 -3
  133. data/lib/rigor/sig_gen/writer.rb +40 -2
  134. data/lib/rigor/signature_path_audit.rb +92 -0
  135. data/lib/rigor/source/constant_path.rb +62 -0
  136. data/lib/rigor/source.rb +1 -0
  137. data/lib/rigor/type/bound_method.rb +2 -11
  138. data/lib/rigor/type/combinator.rb +16 -3
  139. data/lib/rigor/type/constant.rb +2 -11
  140. data/lib/rigor/type/data_class.rb +2 -11
  141. data/lib/rigor/type/data_instance.rb +2 -11
  142. data/lib/rigor/type/hash_shape.rb +2 -11
  143. data/lib/rigor/type/integer_range.rb +2 -11
  144. data/lib/rigor/type/intersection.rb +2 -11
  145. data/lib/rigor/type/nominal.rb +2 -11
  146. data/lib/rigor/type/plain_lattice.rb +37 -0
  147. data/lib/rigor/type/refined.rb +72 -13
  148. data/lib/rigor/type/singleton.rb +2 -11
  149. data/lib/rigor/type/struct_class.rb +75 -0
  150. data/lib/rigor/type/struct_instance.rb +93 -0
  151. data/lib/rigor/type/tuple.rb +5 -15
  152. data/lib/rigor/type.rb +2 -0
  153. data/lib/rigor/version.rb +1 -1
  154. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
  155. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +3 -3
  156. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +3 -3
  157. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +5 -13
  158. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +11 -17
  159. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +7 -10
  160. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +3 -2
  161. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
  162. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +6 -8
  163. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +5 -7
  164. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -2
  165. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +9 -11
  166. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +8 -9
  167. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +13 -12
  168. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +3 -4
  169. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +8 -8
  170. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +9 -11
  171. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +7 -8
  172. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +7 -9
  173. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +12 -13
  174. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +15 -23
  175. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +3 -3
  176. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +3 -3
  177. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +2 -4
  178. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +27 -11
  179. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -1
  180. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -6
  181. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +12 -18
  182. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +5 -5
  183. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -4
  184. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -1
  185. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
  186. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +19 -14
  187. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +0 -1
  188. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +5 -4
  189. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +2 -3
  190. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +7 -11
  191. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +4 -5
  192. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +6 -9
  193. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +5 -15
  194. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +28 -41
  195. data/sig/rigor/scope.rbs +9 -1
  196. data/sig/rigor/type.rbs +36 -1
  197. metadata +49 -1
@@ -0,0 +1,226 @@
1
+ #
2
+ # Rigor-side supplement to the rbs gem's
3
+ # `core/rubygems/*.rbs` declarations.
4
+ #
5
+ # Re-opens the rubygems classes / Gem module to ADD the
6
+ # declarations the upstream RBS omits. Every block below adds
7
+ # new method signatures only — it does NOT redeclare anything
8
+ # the upstream RBS already covers, so the loader does not raise
9
+ # `RBS::DuplicatedDeclarationError`.
10
+ #
11
+ # Driven by the `references/ruby/lib` survey
12
+ # (`docs/CURRENT_WORK.md` § "Queued engine items") which
13
+ # clusters `undefined-method` on Gem-side selectors most-touched
14
+ # by rubygems' own source tree and by bundler. Returns are
15
+ # `untyped` where Rigor cannot infer a precise shape — the goal
16
+ # is to silence false positives, not to author precise sigs.
17
+ #
18
+ module Gem
19
+ # Singleton additions surfacing in the survey but absent from
20
+ # the upstream `core/rubygems/rubygems.rbs`. All are real
21
+ # methods defined in `lib/rubygems/defaults.rb`,
22
+ # `lib/rubygems.rb`, or the platform-specific override files.
23
+ def self.target_rbconfig: () -> untyped
24
+ def self.set_target_rbconfig: (untyped) -> untyped
25
+ def self.load_safe_marshal: () -> untyped
26
+ def self.safe_load_marshal: (untyped) -> untyped
27
+ def self.verbose: () -> bool
28
+ def self.really_verbose: () -> bool
29
+ def self.discover_gems_on_require: () -> bool
30
+ def self.discover_gems_on_require=: (bool) -> bool
31
+ def self.default_user_install: () -> bool
32
+ def self.dynamic_library_suffixes: () -> Array[String]
33
+ def self.extension_api_version: () -> String
34
+ def self.find_default_spec: (untyped) -> untyped?
35
+ def self.install_extension_in_lib: () -> bool
36
+ def self.load_bundler_extensions: () -> untyped
37
+ def self.load_plugin_files: (?untyped plugins) -> untyped
38
+ def self.open_file: (String path, String mode) { (IO) -> untyped } -> untyped
39
+ def self.open_file_with_lock: (String path) { (IO) -> untyped } -> untyped
40
+ def self.state_file: () -> String
41
+ def self.vendor_dir: () -> String
42
+ def self.activate_bin_path: (String name, ?String exec_name, ?untyped requirements) -> String
43
+ end
44
+
45
+ class Gem::Platform
46
+ # `Gem::Platform.new(arg)` is the canonical constructor; the
47
+ # upstream `core/rubygems/platform.rbs` declares the class
48
+ # but no `initialize`, which forces RBS to default to
49
+ # `(): void` and the wrong-arity rule fires for every call
50
+ # site. The accessor / predicate set covers the most-used
51
+ # surface bundler and rubygems internals touch.
52
+ def initialize: (*untyped) -> void
53
+ attr_accessor cpu: String?
54
+ attr_accessor os: String?
55
+ attr_accessor version: String?
56
+ def to_s: () -> String
57
+ def ==: (untyped) -> bool
58
+ def ===: (untyped) -> bool
59
+ def =~: (untyped) -> bool?
60
+ def eql?: (untyped) -> bool
61
+ def hash: () -> Integer
62
+ def match_spec?: (untyped) -> bool
63
+ def match_gem?: (untyped, untyped) -> bool
64
+
65
+ def self.local: () -> Platform
66
+ def self.match_spec?: (untyped) -> bool
67
+ def self.match_gem?: (untyped, untyped) -> bool
68
+ def self.installable?: (untyped) -> bool
69
+ def self.sort_priority: (untyped) -> Integer
70
+ end
71
+
72
+ class Gem::Dependency
73
+ # `Gem::Dependency.new(name, *requirements, type: :runtime)`.
74
+ # Upstream RBS declares the class shell but no constructor; a
75
+ # permissive splat keeps the canonical and the
76
+ # `(name, requirements, type)` invocation forms both silent.
77
+ def initialize: (*untyped) -> void
78
+ attr_accessor name: String
79
+ attr_accessor requirement: Gem::Requirement
80
+ attr_accessor type: Symbol
81
+ attr_accessor prerelease: bool
82
+ def matches_spec?: (untyped) -> bool
83
+ def match?: (*untyped) -> bool
84
+ def to_s: () -> String
85
+ def ==: (untyped) -> bool
86
+ def eql?: (untyped) -> bool
87
+ def hash: () -> Integer
88
+ end
89
+
90
+ class Gem::Specification
91
+ def self.find_all_by_name: (String name, *untyped requirements) -> Array[Gem::Specification]
92
+ def self.stubs: () -> Array[untyped]
93
+ def self.default_stubs: (?String pattern) -> Array[untyped]
94
+ def self.reset: () -> void
95
+ def self.from_yaml: (untyped) -> Gem::Specification
96
+ def self.load: (String path) -> Gem::Specification?
97
+ def self._all: () -> Array[Gem::Specification]
98
+ def self.each: () { (Gem::Specification) -> untyped } -> void
99
+
100
+ # Instance-side surface widely used by bundler / rubygems
101
+ # internals. `attr_accessor` mirrors the writer + reader pair
102
+ # so the survey's `.name=` / `.version=` / `.full_gem_path=` /
103
+ # `.loaded_from=` / `.post_install_message=` writes resolve too.
104
+ def activate: () -> untyped
105
+ def specification_version: () -> Integer
106
+ def files: () -> Array[String]
107
+ def default_gem?: () -> bool
108
+ def spec_file: () -> String
109
+ def runtime_dependencies: () -> Array[Gem::Dependency]
110
+ def required_rubygems_version: () -> Gem::Requirement
111
+ def required_ruby_version: () -> Gem::Requirement
112
+ def require_paths: () -> Array[String]
113
+ attr_accessor name: String
114
+ attr_accessor version: Gem::Version
115
+ attr_accessor full_gem_path: String
116
+ attr_accessor loaded_from: String?
117
+ attr_accessor post_install_message: String?
118
+ end
119
+
120
+ class Gem::BasicSpecification
121
+ def version: () -> Gem::Version
122
+ def executables: () -> Array[String]
123
+ def name: () -> String
124
+ def full_name: () -> String
125
+ def gem_dir: () -> String
126
+ end
127
+
128
+ class Gem::Version
129
+ def segments: () -> Array[untyped]
130
+ end
131
+
132
+ class Gem::Requirement
133
+ def requirements: () -> Array[untyped]
134
+ def self.create: (*untyped) -> Gem::Requirement
135
+ end
136
+
137
+ class Gem::ConfigFile
138
+ def []: (untyped) -> untyped
139
+ def []=: (untyped, untyped) -> untyped
140
+ def write: () -> void
141
+ def verbose: () -> bool
142
+ def verbose=: (bool) -> bool
143
+ def really_verbose: () -> bool
144
+ def ssl_ca_cert: () -> String?
145
+ def ssl_client_cert: () -> String?
146
+ def ssl_verify_mode: () -> Integer?
147
+ def credentials_path: () -> String
148
+ def api_keys: () -> Hash[String, String]
149
+ def cert_expiration_length_days: () -> Integer
150
+ def sources: () -> Array[String]
151
+ def update_sources=: (bool) -> bool
152
+ def unset_api_key!: () -> bool
153
+ def state_file_writable?: () -> bool
154
+ def set_api_key: (untyped host, untyped key) -> untyped
155
+ def rubygems_api_key: () -> String?
156
+ def rubygems_api_key=: (String?) -> String?
157
+ def last_update_check: () -> Integer
158
+ def last_update_check=: (Integer) -> Integer
159
+ def ipv4_fallback_enabled: () -> bool
160
+ def install_extension_in_lib: () -> bool
161
+ def each: () { ([String, untyped]) -> untyped } -> void
162
+ end
163
+
164
+ class Gem::LoadError < LoadError
165
+ attr_accessor name: String
166
+ attr_accessor requirement: Gem::Requirement
167
+ end
168
+
169
+ class Gem::SourceList
170
+ include Enumerable[untyped]
171
+ def initialize: () -> void
172
+ def include?: (untyped) -> bool
173
+ def each: () { (untyped) -> untyped } -> void
174
+ def to_a: () -> Array[untyped]
175
+ def from: (untyped) -> SourceList
176
+ end
177
+
178
+ class Gem::RequestSet
179
+ def initialize: (*untyped) -> void
180
+ def import: (untyped) -> untyped
181
+ def source_set: () -> untyped
182
+ def installed_gems: () -> Hash[String, untyped]
183
+ def install: (?untyped options) { (?untyped) -> untyped } -> untyped
184
+ def install_from_gemdeps: (untyped options) { (?untyped) -> untyped } -> untyped
185
+ def resolve: (?untyped) -> untyped
186
+ def resolve_current: () -> untyped
187
+ def resolve_dependencies: () -> untyped
188
+ def errors: () -> Array[untyped]
189
+ attr_accessor remote: bool
190
+ attr_accessor development: bool
191
+ attr_accessor development_shallow: bool
192
+ attr_accessor ignore_dependencies: bool
193
+ attr_accessor prerelease: bool
194
+ attr_accessor soft_missing: bool
195
+ attr_accessor without_groups: Array[Symbol]
196
+ attr_accessor always_install: untyped
197
+ attr_accessor installing: bool
198
+ end
199
+
200
+ class Gem::DependencyInstaller
201
+ def initialize: (*untyped) -> void
202
+ def install: (untyped, ?untyped) -> Array[untyped]
203
+ def installed_gems: () -> Array[untyped]
204
+ def errors: () -> Array[untyped]
205
+ end
206
+
207
+ class Gem::Uninstaller
208
+ def initialize: (*untyped) -> void
209
+ def uninstall: () -> void
210
+ end
211
+
212
+ class Gem::PathSupport
213
+ def initialize: (*untyped) -> void
214
+ end
215
+
216
+ class Gem::Installer
217
+ def initialize: (*untyped) -> void
218
+ end
219
+
220
+ class Gem::DependencyInstaller
221
+ def initialize: (*untyped) -> void
222
+ end
223
+
224
+ class Gem::MissingSpecError < StandardError
225
+ def initialize: (*untyped) -> void
226
+ 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
@@ -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