rigortype 0.1.1 → 0.1.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -0
  3. data/data/builtins/ruby_core/range.yml +6 -4
  4. data/data/builtins/ruby_core/string.yml +15 -10
  5. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +116 -0
  6. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +123 -0
  7. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +118 -0
  8. data/lib/rigor/analysis/check_rules.rb +346 -18
  9. data/lib/rigor/analysis/dependency_source_inference/builder.rb +87 -0
  10. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +72 -0
  11. data/lib/rigor/analysis/dependency_source_inference/index.rb +110 -0
  12. data/lib/rigor/analysis/dependency_source_inference/walker.rb +200 -0
  13. data/lib/rigor/analysis/dependency_source_inference.rb +37 -0
  14. data/lib/rigor/analysis/rule_catalog.rb +343 -0
  15. data/lib/rigor/analysis/runner.rb +96 -6
  16. data/lib/rigor/cache/descriptor.rb +58 -5
  17. data/lib/rigor/cli/diff_command.rb +169 -0
  18. data/lib/rigor/cli/explain_command.rb +129 -0
  19. data/lib/rigor/cli.rb +18 -1
  20. data/lib/rigor/configuration/dependencies.rb +235 -0
  21. data/lib/rigor/configuration/severity_profile.rb +18 -3
  22. data/lib/rigor/configuration.rb +53 -13
  23. data/lib/rigor/environment.rb +16 -4
  24. data/lib/rigor/flow_contribution/merger.rb +4 -0
  25. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +104 -12
  26. data/lib/rigor/inference/method_dispatcher.rb +87 -0
  27. data/lib/rigor/inference/scope_indexer.rb +171 -2
  28. data/lib/rigor/inference/statement_evaluator.rb +65 -1
  29. data/lib/rigor/plugin/io_boundary.rb +92 -19
  30. data/lib/rigor/plugin/manifest.rb +26 -5
  31. data/lib/rigor/plugin/trust_policy.rb +30 -7
  32. data/lib/rigor/scope.rb +30 -5
  33. data/lib/rigor/version.rb +1 -1
  34. data/sig/rigor/environment.rbs +3 -2
  35. data/sig/rigor/scope.rbs +3 -0
  36. metadata +13 -1
@@ -785,6 +785,7 @@ module Rigor
785
785
  evaluate_block_if_present(node)
786
786
  post_scope = record_closure_escape_if_any(node)
787
787
  post_scope = apply_rbs_extended_assertions(node, post_scope)
788
+ post_scope = apply_plugin_assertions(node, post_scope)
788
789
  post_scope = apply_rspec_matcher_narrowing(node, post_scope)
789
790
  [call_type, post_scope]
790
791
  end
@@ -978,6 +979,61 @@ module Rigor
978
979
  end
979
980
  end
980
981
 
982
+ # ADR-7 § "Slice 4-A" / T.bind priority slice 2 — applies
983
+ # the post-return facts plugin contributions produce. This
984
+ # is the sibling of {apply_rbs_extended_assertions}: the
985
+ # carrier (`Rigor::FlowContribution::Fact`) and the
986
+ # downstream narrowing path (`apply_post_return_fact` →
987
+ # `apply_self_post_return_fact`) are the same; only the
988
+ # *source* of the bundle changes (RBS::Extended vs the
989
+ # registered plugins' `flow_contribution_for`).
990
+ #
991
+ # `:self`-targeted facts narrow `scope.self_type` for the
992
+ # surrounding scope. In a block body, the surrounding
993
+ # scope is the block's own scope, so the narrowing applies
994
+ # to the rest of the block — exactly the contract Sorbet's
995
+ # `T.bind(self, T)` commits to.
996
+ #
997
+ # `:parameter`-targeted facts only land when the called
998
+ # method has an authoritative RBS sig (via
999
+ # `resolve_call_method`); plugins recognising their own
1000
+ # synthetic call shapes (e.g. `T.assert_type!`) have no
1001
+ # method_def and the parameter facts silently skip — the
1002
+ # plugin's own diagnostics_for_file path covers those
1003
+ # cases. The full plugin-side parameter-targeting story
1004
+ # (PHPStan-style Type-Specifying Extensions on
1005
+ # plugin-recognised calls) lives behind a follow-up slice
1006
+ # that introduces `:local` / `:argument_at` target kinds.
1007
+ def apply_plugin_assertions(call_node, current_scope)
1008
+ registry = current_scope.environment&.plugin_registry
1009
+ return current_scope if registry.nil? || registry.empty?
1010
+
1011
+ contributions = collect_plugin_contributions(registry, call_node, current_scope)
1012
+ return current_scope if contributions.empty?
1013
+
1014
+ result = Rigor::FlowContribution::Merger.merge(contributions)
1015
+ post_return = result.post_return_facts
1016
+ return current_scope if post_return.empty?
1017
+
1018
+ method_def = resolve_call_method(call_node, current_scope)
1019
+ post_return.reduce(current_scope) do |scope_acc, fact|
1020
+ apply_post_return_fact(fact, call_node, scope_acc, method_def)
1021
+ end
1022
+ end
1023
+
1024
+ # Walks the registry and collects each plugin's
1025
+ # `flow_contribution_for` result, swallowing per-plugin
1026
+ # exceptions so a buggy plugin can't abort the assertion
1027
+ # path. Mirrors `MethodDispatcher.collect_plugin_contributions`
1028
+ # exactly — the two paths consume the same hook.
1029
+ def collect_plugin_contributions(registry, call_node, current_scope)
1030
+ registry.plugins.filter_map do |plugin|
1031
+ plugin.flow_contribution_for(call_node: call_node, scope: current_scope)
1032
+ rescue StandardError
1033
+ nil
1034
+ end
1035
+ end
1036
+
981
1037
  def resolve_call_method(call_node, current_scope) # rubocop:disable Metrics/PerceivedComplexity
982
1038
  receiver_node = call_node.receiver
983
1039
  receiver_type =
@@ -1064,7 +1120,15 @@ module Rigor
1064
1120
  end
1065
1121
  end
1066
1122
 
1067
- def lookup_post_return_arg(call_node, method_def, target_name)
1123
+ def lookup_post_return_arg(call_node, method_def, target_name) # rubocop:disable Metrics/CyclomaticComplexity
1124
+ # Plugin-source contributions arrive without an
1125
+ # authoritative method_def (the plugin recognised the
1126
+ # call shape directly). Parameter-targeting falls back
1127
+ # to "no narrow" in that case — the wider plugin-side
1128
+ # parameter mapping (`:local` / `:argument_at`) is a
1129
+ # follow-up slice.
1130
+ return nil if method_def.nil?
1131
+
1068
1132
  arguments = call_node.arguments&.arguments || []
1069
1133
  method_def.method_types.each do |mt|
1070
1134
  params = mt.type.required_positionals + mt.type.optional_positionals
@@ -27,20 +27,37 @@ module Rigor
27
27
  # the file's contents, and adds a digest-keyed
28
28
  # {Cache::Descriptor::FileEntry} to the boundary's
29
29
  # accumulated descriptor.
30
- # - `#open_url(url)` — always raises {AccessDeniedError} while
31
- # `network_policy` is `:disabled` (the only setting in slice
32
- # 2). The hook exists so slices 3-6 can layer richer access
33
- # policy without re-defining the API.
30
+ # - `#open_url(url)` — fetches the URL when the policy
31
+ # permits it (`network_policy: :allowlist` plus an
32
+ # `allowed_url_hosts` match) and raises
33
+ # {AccessDeniedError} otherwise. v0.1.2 ships the
34
+ # allowlist surface; the default project policy still
35
+ # has `network_policy: :disabled` so plugins that want
36
+ # network access opt in explicitly through
37
+ # `.rigor.yml`'s `plugins_io.network: allowlist` plus
38
+ # `plugins_io.allowed_url_hosts: [...]`. The HTTP fetch
39
+ # is GET-only over HTTPS, capped at {URL_TIMEOUT_SECONDS}
40
+ # wall time and {URL_MAX_BYTES} body size; non-2xx
41
+ # responses raise {AccessDeniedError} so plugin code
42
+ # doesn't have to rescue mid-build.
34
43
  # - `#cache_descriptor` — flushes the accumulated entries into
35
44
  # a fresh {Cache::Descriptor} for the contribution that
36
- # built it.
45
+ # built it. URL fetches contribute `ConfigEntry` rows
46
+ # keyed `"url:#{url}"` with the response body's SHA-256
47
+ # so contributions invalidate when the remote document
48
+ # changes.
37
49
  class IoBoundary
50
+ URL_TIMEOUT_SECONDS = 10
51
+ URL_MAX_BYTES = 10 * 1024 * 1024
52
+
38
53
  attr_reader :policy, :plugin_id
39
54
 
40
- def initialize(policy:, plugin_id:)
55
+ def initialize(policy:, plugin_id:, http_client: DefaultHttpClient.new)
41
56
  @policy = policy
42
57
  @plugin_id = plugin_id.to_s.dup.freeze
43
58
  @file_entries = {}
59
+ @config_entries = {}
60
+ @http_client = http_client
44
61
  @mutex = Mutex.new
45
62
  end
46
63
 
@@ -64,30 +81,39 @@ module Rigor
64
81
  contents
65
82
  end
66
83
 
67
- # Slice 2 stub: every URL access is denied while
68
- # `network_policy` is `:disabled`. Slices that need to relax
69
- # the rule (e.g. for opt-in offline-replay caches) will lift
70
- # the policy gate; the API does not change.
84
+ # Fetches the URL when the policy permits it. Returns the
85
+ # response body. Raises {AccessDeniedError} when the policy
86
+ # is `:disabled`, the URL scheme is not `https`, the host is
87
+ # not on the allowlist, the response is non-2xx, the body
88
+ # exceeds {URL_MAX_BYTES}, or the request times out
89
+ # ({URL_TIMEOUT_SECONDS}). On success, records a
90
+ # `ConfigEntry` keyed `"url:#{url}"` with the body's
91
+ # SHA-256 so the cache descriptor invalidates if the remote
92
+ # document changes.
71
93
  def open_url(url)
72
- unless @policy.network_allowed?
94
+ url_string = url.to_s
95
+ unless @policy.allow_url?(url_string)
73
96
  raise AccessDeniedError.new(
74
97
  "plugin #{@plugin_id.inspect} cannot open URL #{url.inspect}: " \
75
- "network access is disabled during analysis",
98
+ "URL is not permitted by the active TrustPolicy " \
99
+ "(network_policy=#{@policy.network_policy} allowed_url_hosts=#{@policy.allowed_url_hosts.inspect})",
76
100
  reason: :network_disabled,
77
- resource: url.to_s
101
+ resource: url_string
78
102
  )
79
103
  end
80
104
 
81
- raise NotImplementedError, "URL fetch surface is reserved; slice 2 only ships the deny path"
105
+ body = @http_client.get(url_string, timeout: URL_TIMEOUT_SECONDS, max_bytes: URL_MAX_BYTES)
106
+ record_url_entry(url_string, body)
107
+ body
82
108
  end
83
109
 
84
110
  # @return [Rigor::Cache::Descriptor] frozen snapshot of every
85
- # file the boundary has read so far. Calling this multiple
86
- # times yields equal descriptors; subsequent reads expand
87
- # the underlying record table.
111
+ # file / URL the boundary has read so far. Calling this
112
+ # multiple times yields equal descriptors; subsequent
113
+ # reads expand the underlying record tables.
88
114
  def cache_descriptor
89
- entries = @mutex.synchronize { @file_entries.values.dup }
90
- Cache::Descriptor.new(files: entries)
115
+ files, configs = @mutex.synchronize { [@file_entries.values.dup, @config_entries.values.dup] }
116
+ Cache::Descriptor.new(files: files, configs: configs)
91
117
  end
92
118
 
93
119
  private
@@ -97,6 +123,53 @@ module Rigor
97
123
  entry = Cache::Descriptor::FileEntry.new(path: path, comparator: :digest, value: digest)
98
124
  @mutex.synchronize { @file_entries[path] = entry }
99
125
  end
126
+
127
+ def record_url_entry(url, body)
128
+ digest = Digest::SHA256.hexdigest(body)
129
+ key = "url:#{url}"
130
+ entry = Cache::Descriptor::ConfigEntry.new(key: key, value_hash: digest)
131
+ @mutex.synchronize { @config_entries[key] = entry }
132
+ end
133
+ end
134
+
135
+ # Default HTTP client wrapping `Net::HTTP`. Wraps a single
136
+ # `GET` over HTTPS. Specs inject a fake client that conforms
137
+ # to the same `#get(url, timeout:, max_bytes:)` shape so the
138
+ # tests don't require network access.
139
+ class DefaultHttpClient
140
+ # rubocop:disable Metrics/MethodLength
141
+ def get(url, timeout:, max_bytes:)
142
+ require "net/http"
143
+ require "uri"
144
+
145
+ uri = URI.parse(url)
146
+ body = +""
147
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true,
148
+ open_timeout: timeout,
149
+ read_timeout: timeout) do |http|
150
+ http.request_get(uri.request_uri) do |response|
151
+ unless response.is_a?(Net::HTTPSuccess)
152
+ raise Plugin::AccessDeniedError.new(
153
+ "URL #{url.inspect} returned non-success status #{response.code}",
154
+ reason: :url_fetch_failed,
155
+ resource: url
156
+ )
157
+ end
158
+ response.read_body do |chunk|
159
+ body << chunk
160
+ if body.bytesize > max_bytes
161
+ raise Plugin::AccessDeniedError.new(
162
+ "URL #{url.inspect} body exceeds #{max_bytes} bytes",
163
+ reason: :url_body_too_large,
164
+ resource: url
165
+ )
166
+ end
167
+ end
168
+ end
169
+ end
170
+ body
171
+ end
172
+ # rubocop:enable Metrics/MethodLength
100
173
  end
101
174
  end
102
175
  end
@@ -37,26 +37,29 @@ module Rigor
37
37
  end
38
38
  end
39
39
 
40
- attr_reader :id, :version, :description, :protocols, :config_schema, :produces, :consumes
40
+ attr_reader :id, :version, :description, :protocols, :config_schema, :produces, :consumes,
41
+ :owns_receivers
41
42
 
42
43
  def initialize( # rubocop:disable Metrics/ParameterLists
43
44
  id:, version:,
44
45
  description: nil, protocols: [], config_schema: {},
45
- produces: [], consumes: []
46
+ produces: [], consumes: [], owns_receivers: []
46
47
  )
47
48
  validate_id!(id)
48
49
  validate_version!(version)
49
50
  validate_protocols!(protocols)
50
51
  validate_config_schema!(config_schema)
51
52
  validate_produces!(produces)
53
+ validate_owns_receivers!(owns_receivers)
52
54
 
53
- assign_fields(id, version, description, protocols, config_schema, produces, consumes)
55
+ assign_fields(id, version, description, protocols, config_schema, produces, consumes, owns_receivers)
54
56
  freeze
55
57
  end
56
58
 
57
59
  private
58
60
 
59
- def assign_fields(id, version, description, protocols, config_schema, produces, consumes) # rubocop:disable Metrics/ParameterLists
61
+ # rubocop:disable Metrics/ParameterLists,Metrics/AbcSize
62
+ def assign_fields(id, version, description, protocols, config_schema, produces, consumes, owns_receivers)
60
63
  @id = id.dup.freeze
61
64
  @version = version.dup.freeze
62
65
  @description = description.nil? ? nil : description.to_s.dup.freeze
@@ -64,7 +67,9 @@ module Rigor
64
67
  @config_schema = config_schema.to_h { |k, v| [k.to_s.dup.freeze, v.to_sym] }.freeze
65
68
  @produces = produces.map(&:to_sym).freeze
66
69
  @consumes = coerce_consumes(consumes)
70
+ @owns_receivers = owns_receivers.map { |c| c.to_s.dup.freeze }.freeze
67
71
  end
72
+ # rubocop:enable Metrics/ParameterLists,Metrics/AbcSize
68
73
 
69
74
  public
70
75
 
@@ -99,7 +104,8 @@ module Rigor
99
104
  "protocols" => protocols.map(&:to_s),
100
105
  "config_schema" => config_schema.to_h { |k, v| [k, v.to_s] },
101
106
  "produces" => produces.map(&:to_s),
102
- "consumes" => consumes.map { |c| consumption_hash(c) }
107
+ "consumes" => consumes.map { |c| consumption_hash(c) },
108
+ "owns_receivers" => owns_receivers
103
109
  }
104
110
  end
105
111
 
@@ -166,6 +172,21 @@ module Rigor
166
172
  raise ArgumentError, "plugin manifest produces must be an Array of Symbol/String, got #{produces.inspect}"
167
173
  end
168
174
 
175
+ # ADR-10 5a — `owns_receivers:` declares the class names
176
+ # this plugin claims sole ownership of. The dispatcher's
177
+ # dependency-source-inference tier consults this list
178
+ # before consulting its own catalog: receivers owned by a
179
+ # registered plugin (directly or via subclass) decline,
180
+ # so plugin contributions stay authoritative for those
181
+ # types.
182
+ def validate_owns_receivers!(owns_receivers)
183
+ return if owns_receivers.is_a?(Array) && owns_receivers.all? { |c| c.is_a?(String) && !c.empty? }
184
+
185
+ raise ArgumentError,
186
+ "plugin manifest owns_receivers must be an Array of non-empty String, " \
187
+ "got #{owns_receivers.inspect}"
188
+ end
189
+
169
190
  def coerce_consumes(consumes)
170
191
  unless consumes.is_a?(Array)
171
192
  raise ArgumentError, "plugin manifest consumes must be an Array, got #{consumes.inspect}"
@@ -32,15 +32,18 @@ module Rigor
32
32
  # `Gemfile.lock`, and each trusted gem's
33
33
  # `Gem::Specification#full_gem_path`. The user extends this
34
34
  # with `.rigor.yml`'s `plugins_io.allowed_paths:`.
35
- # - `network_policy`: `:disabled` in slice 2; the only value
36
- # accepted today. Plugin {IoBoundary#open_url} always raises
37
- # while the policy is `:disabled`.
35
+ # - `network_policy`: one of {VALID_NETWORK_POLICIES}.
36
+ # `:disabled` (default) makes {IoBoundary#open_url} always
37
+ # raise. `:allowlist` (v0.1.2) consults `allowed_url_hosts`
38
+ # on every fetch — the hostname must be on the list and
39
+ # the URL scheme MUST be `https`. The list of allowed hosts
40
+ # is exact-match (no wildcards in v0.1.2).
38
41
  class TrustPolicy
39
- VALID_NETWORK_POLICIES = %i[disabled].freeze
42
+ VALID_NETWORK_POLICIES = %i[disabled allowlist].freeze
40
43
 
41
- attr_reader :trusted_gems, :allowed_read_roots, :network_policy
44
+ attr_reader :trusted_gems, :allowed_read_roots, :network_policy, :allowed_url_hosts
42
45
 
43
- def initialize(trusted_gems: [], allowed_read_roots: [], network_policy: :disabled)
46
+ def initialize(trusted_gems: [], allowed_read_roots: [], network_policy: :disabled, allowed_url_hosts: [])
44
47
  validate_network_policy!(network_policy)
45
48
 
46
49
  @trusted_gems = trusted_gems.map { |g| g.to_s.dup.freeze }.uniq.sort.freeze
@@ -50,6 +53,7 @@ module Rigor
50
53
  .sort
51
54
  .freeze
52
55
  @network_policy = network_policy
56
+ @allowed_url_hosts = allowed_url_hosts.map { |h| h.to_s.downcase.dup.freeze }.uniq.sort.freeze
53
57
  freeze
54
58
  end
55
59
 
@@ -67,6 +71,24 @@ module Rigor
67
71
  @network_policy != :disabled
68
72
  end
69
73
 
74
+ # @param url [String, URI]
75
+ # @return [Boolean] true when the URL scheme is `https` and
76
+ # the parsed hostname is in `allowed_url_hosts`. Always
77
+ # `false` while `network_policy` is `:disabled`.
78
+ def allow_url?(url)
79
+ return false if @network_policy == :disabled
80
+ return false if @allowed_url_hosts.empty?
81
+
82
+ require "uri"
83
+ uri = url.is_a?(URI::Generic) ? url : URI.parse(url.to_s)
84
+ return false unless uri.is_a?(URI::HTTPS)
85
+ return false if uri.host.nil?
86
+
87
+ @allowed_url_hosts.include?(uri.host.downcase)
88
+ rescue URI::InvalidURIError
89
+ false
90
+ end
91
+
70
92
  def gem_trusted?(name)
71
93
  @trusted_gems.include?(name.to_s)
72
94
  end
@@ -75,7 +97,8 @@ module Rigor
75
97
  {
76
98
  "trusted_gems" => trusted_gems,
77
99
  "allowed_read_roots" => allowed_read_roots,
78
- "network_policy" => network_policy.to_s
100
+ "network_policy" => network_policy.to_s,
101
+ "allowed_url_hosts" => allowed_url_hosts
79
102
  }
80
103
  end
81
104
 
data/lib/rigor/scope.rb CHANGED
@@ -20,7 +20,7 @@ module Rigor
20
20
  :ivars, :cvars, :globals,
21
21
  :class_ivars, :class_cvars, :program_globals,
22
22
  :discovered_classes, :in_source_constants, :discovered_methods,
23
- :discovered_def_nodes
23
+ :discovered_def_nodes, :discovered_method_visibilities
24
24
 
25
25
  EMPTY_DECLARED_TYPES = {}.compare_by_identity.freeze
26
26
  EMPTY_VAR_BINDINGS = {}.freeze
@@ -47,7 +47,8 @@ module Rigor
47
47
  discovered_classes: EMPTY_VAR_BINDINGS,
48
48
  in_source_constants: EMPTY_VAR_BINDINGS,
49
49
  discovered_methods: EMPTY_CLASS_BINDINGS,
50
- discovered_def_nodes: EMPTY_CLASS_BINDINGS
50
+ discovered_def_nodes: EMPTY_CLASS_BINDINGS,
51
+ discovered_method_visibilities: EMPTY_CLASS_BINDINGS
51
52
  )
52
53
  @environment = environment
53
54
  @locals = locals
@@ -64,6 +65,7 @@ module Rigor
64
65
  @in_source_constants = in_source_constants
65
66
  @discovered_methods = discovered_methods
66
67
  @discovered_def_nodes = discovered_def_nodes
68
+ @discovered_method_visibilities = discovered_method_visibilities
67
69
  freeze
68
70
  end
69
71
 
@@ -268,6 +270,26 @@ module Rigor
268
270
  rebuild(discovered_def_nodes: table)
269
271
  end
270
272
 
273
+ # v0.1.2 — per-class table mapping `method_name (Symbol) →
274
+ # :public | :private | :protected`. Populated by
275
+ # `ScopeIndexer` for every `def` it sees inside a class
276
+ # body, with the visibility taken from the surrounding
277
+ # `private` / `protected` / `public` modifier state plus
278
+ # any post-hoc `private :name, ...` named-argument calls.
279
+ # Consumed by the `def.method-visibility-mismatch` rule
280
+ # so explicit-non-self calls to a private method surface
281
+ # a diagnostic.
282
+ def discovered_method_visibility(class_name, method_name)
283
+ table = @discovered_method_visibilities[class_name.to_s]
284
+ return nil unless table
285
+
286
+ table[method_name.to_sym]
287
+ end
288
+
289
+ def with_discovered_method_visibilities(table)
290
+ rebuild(discovered_method_visibilities: table)
291
+ end
292
+
271
293
  def facts_for(target: nil, bucket: nil)
272
294
  fact_store.facts_for(target: target, bucket: bucket)
273
295
  end
@@ -334,7 +356,8 @@ module Rigor
334
356
  declared_types: @declared_types, ivars: @ivars, cvars: @cvars, globals: @globals,
335
357
  class_ivars: @class_ivars, class_cvars: @class_cvars, program_globals: @program_globals,
336
358
  discovered_classes: @discovered_classes, in_source_constants: @in_source_constants,
337
- discovered_methods: @discovered_methods, discovered_def_nodes: @discovered_def_nodes
359
+ discovered_methods: @discovered_methods, discovered_def_nodes: @discovered_def_nodes,
360
+ discovered_method_visibilities: @discovered_method_visibilities
338
361
  )
339
362
  self.class.new(
340
363
  environment: environment, locals: locals,
@@ -346,7 +369,8 @@ module Rigor
346
369
  discovered_classes: discovered_classes,
347
370
  in_source_constants: in_source_constants,
348
371
  discovered_methods: discovered_methods,
349
- discovered_def_nodes: discovered_def_nodes
372
+ discovered_def_nodes: discovered_def_nodes,
373
+ discovered_method_visibilities: discovered_method_visibilities
350
374
  )
351
375
  end
352
376
 
@@ -371,7 +395,8 @@ module Rigor
371
395
  discovered_classes: discovered_classes,
372
396
  in_source_constants: in_source_constants,
373
397
  discovered_methods: discovered_methods,
374
- discovered_def_nodes: discovered_def_nodes
398
+ discovered_def_nodes: discovered_def_nodes,
399
+ discovered_method_visibilities: discovered_method_visibilities
375
400
  )
376
401
  end
377
402
  end
data/lib/rigor/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
@@ -7,11 +7,12 @@ module Rigor
7
7
  attr_reader class_registry: ClassRegistry
8
8
  attr_reader rbs_loader: RbsLoader?
9
9
  attr_reader plugin_registry: untyped?
10
+ attr_reader dependency_source_index: untyped?
10
11
 
11
12
  def self.default: () -> Environment
12
- def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?) -> Environment
13
+ def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?) -> Environment
13
14
 
14
- def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?) -> void
15
+ def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?) -> void
15
16
  def nominal_for_name: (String | Symbol name) -> Type::Nominal?
16
17
  def singleton_for_name: (String | Symbol name) -> Type::Singleton?
17
18
  def constant_for_name: (String | Symbol name) -> Type::t?
data/sig/rigor/scope.rbs CHANGED
@@ -15,6 +15,7 @@ module Rigor
15
15
  attr_reader in_source_constants: Hash[String, Type::t]
16
16
  attr_reader discovered_methods: Hash[String, Hash[Symbol, Symbol]]
17
17
  attr_reader discovered_def_nodes: Hash[String, Hash[Symbol, untyped]]
18
+ attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]]
18
19
 
19
20
  def self.empty: (?environment: Environment) -> Scope
20
21
 
@@ -39,6 +40,8 @@ module Rigor
39
40
  def with_discovered_def_nodes: (Hash[String, Hash[Symbol, untyped]] table) -> Scope
40
41
  def user_def_for: (String | Symbol class_name, String | Symbol method_name) -> untyped?
41
42
  def top_level_def_for: (String | Symbol method_name) -> untyped?
43
+ def with_discovered_method_visibilities: (Hash[String, Hash[Symbol, Symbol]] table) -> Scope
44
+ def discovered_method_visibility: (String | Symbol class_name, String | Symbol method_name) -> Symbol?
42
45
  def with_fact: (Analysis::FactStore::Fact fact) -> Scope
43
46
  def with_self_type: (Type::t? type) -> Scope
44
47
  def with_declared_types: (Hash[untyped, Type::t] table) -> Scope
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -184,9 +184,18 @@ files:
184
184
  - exe/rigor
185
185
  - lib/rigor.rb
186
186
  - lib/rigor/analysis/check_rules.rb
187
+ - lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb
188
+ - lib/rigor/analysis/check_rules/dead_assignment_collector.rb
189
+ - lib/rigor/analysis/check_rules/ivar_write_collector.rb
190
+ - lib/rigor/analysis/dependency_source_inference.rb
191
+ - lib/rigor/analysis/dependency_source_inference/builder.rb
192
+ - lib/rigor/analysis/dependency_source_inference/gem_resolver.rb
193
+ - lib/rigor/analysis/dependency_source_inference/index.rb
194
+ - lib/rigor/analysis/dependency_source_inference/walker.rb
187
195
  - lib/rigor/analysis/diagnostic.rb
188
196
  - lib/rigor/analysis/fact_store.rb
189
197
  - lib/rigor/analysis/result.rb
198
+ - lib/rigor/analysis/rule_catalog.rb
190
199
  - lib/rigor/analysis/runner.rb
191
200
  - lib/rigor/ast.rb
192
201
  - lib/rigor/ast/type_node.rb
@@ -203,12 +212,15 @@ files:
203
212
  - lib/rigor/cache/rbs_known_class_names.rb
204
213
  - lib/rigor/cache/store.rb
205
214
  - lib/rigor/cli.rb
215
+ - lib/rigor/cli/diff_command.rb
216
+ - lib/rigor/cli/explain_command.rb
206
217
  - lib/rigor/cli/type_of_command.rb
207
218
  - lib/rigor/cli/type_of_renderer.rb
208
219
  - lib/rigor/cli/type_scan_command.rb
209
220
  - lib/rigor/cli/type_scan_renderer.rb
210
221
  - lib/rigor/cli/type_scan_report.rb
211
222
  - lib/rigor/configuration.rb
223
+ - lib/rigor/configuration/dependencies.rb
212
224
  - lib/rigor/configuration/severity_profile.rb
213
225
  - lib/rigor/environment.rb
214
226
  - lib/rigor/environment/class_registry.rb