rigortype 0.1.11 → 0.1.12
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/lib/rigor/analysis/erb_template_detector.rb +38 -0
- data/lib/rigor/analysis/runner.rb +6 -1
- data/lib/rigor/analysis/worker_session.rb +6 -1
- data/lib/rigor/cli/plugins_command.rb +308 -0
- data/lib/rigor/cli/plugins_renderer.rb +173 -0
- data/lib/rigor/cli.rb +28 -0
- data/lib/rigor/inference/block_parameter_binder.rb +35 -0
- data/lib/rigor/inference/expression_typer.rb +69 -30
- data/lib/rigor/inference/indexed_narrowing.rb +187 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +24 -0
- data/lib/rigor/inference/method_dispatcher.rb +23 -0
- data/lib/rigor/inference/mutation_widening.rb +285 -0
- data/lib/rigor/inference/narrowing.rb +72 -4
- data/lib/rigor/inference/scope_indexer.rb +409 -12
- data/lib/rigor/inference/statement_evaluator.rb +256 -4
- data/lib/rigor/scope.rb +181 -4
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +22 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +94 -6
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +11 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +7 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +135 -11
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +94 -43
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +138 -35
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +17 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +10 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +13 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +6 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +83 -7
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +4 -1
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +16 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +81 -5
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +11 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +194 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +264 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +100 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +175 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +64 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +1107 -59
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +81 -4
- data/sig/rigor/scope.rbs +22 -0
- metadata +9 -1
|
@@ -4,6 +4,7 @@ require "rigor/plugin"
|
|
|
4
4
|
|
|
5
5
|
require_relative "rails_routes/helper_table"
|
|
6
6
|
require_relative "rails_routes/routes_parser"
|
|
7
|
+
require_relative "rails_routes/helper_discoverer"
|
|
7
8
|
require_relative "rails_routes/analyzer"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
@@ -52,32 +53,104 @@ module Rigor
|
|
|
52
53
|
class RailsRoutes < Rigor::Plugin::Base
|
|
53
54
|
manifest(
|
|
54
55
|
id: "rails-routes",
|
|
55
|
-
|
|
56
|
+
# Bumped 2026-05-28 — GitLab FOSS sweep adds: (a)
|
|
57
|
+
# `draw_all :name` support (action_dispatch-draw_all
|
|
58
|
+
# gem; single-file load semantics matching `draw :name`);
|
|
59
|
+
# (b) keyword-style `scope(path: ':project_id',
|
|
60
|
+
# as: :project)` — path read from the `:path` keyword,
|
|
61
|
+
# not only from the positional first arg; (c) detection
|
|
62
|
+
# of iterative `direct(name.sub(FROM, TO)) do ... end`
|
|
63
|
+
# alias-generation idiom — generates `<TO>_*` aliases
|
|
64
|
+
# for every registered `<FROM>_*` helper. GitLab uses
|
|
65
|
+
# this to shorten `namespace_project_*` → `project_*`.
|
|
66
|
+
version: "0.27.0",
|
|
56
67
|
description: "Validates Rails route-helper calls against `config/routes.rb`.",
|
|
57
68
|
config_schema: {
|
|
58
|
-
"routes_file" => :string
|
|
69
|
+
"routes_file" => :string,
|
|
70
|
+
"helper_paths" => :array
|
|
59
71
|
},
|
|
60
72
|
produces: [:helper_table]
|
|
61
73
|
)
|
|
62
74
|
|
|
63
75
|
DEFAULT_ROUTES_FILE = "config/routes.rb"
|
|
64
76
|
|
|
77
|
+
# The directories `HelperDiscoverer` walks for project-
|
|
78
|
+
# defined `*_path` / `*_url` methods. Default to the whole
|
|
79
|
+
# `app/` tree — the suffix filter inside the discoverer
|
|
80
|
+
# keeps the registered set tight, and real-world Rails
|
|
81
|
+
# apps routinely keep URL builders under `app/controllers`
|
|
82
|
+
# (private `def page_url`, `def callback_url` shapes),
|
|
83
|
+
# `app/lib` (Mastodon's `TranslationService::DeepL#base_url`),
|
|
84
|
+
# `app/services` (`SoftwareUpdateCheckService#api_url`),
|
|
85
|
+
# `app/serializers`, `app/presenters`, `app/decorators`,
|
|
86
|
+
# not only `app/helpers/`. Walking the whole tree is the
|
|
87
|
+
# honest answer to "does this `_path` / `_url` name exist
|
|
88
|
+
# anywhere in the project?"; the cost is a one-time Prism
|
|
89
|
+
# parse per file at startup, which is bounded.
|
|
90
|
+
DEFAULT_HELPER_PATHS = ["app"].freeze
|
|
91
|
+
|
|
65
92
|
# Cached producer — reads `config/routes.rb` through
|
|
66
93
|
# the trusted `IoBoundary` and parses through
|
|
67
94
|
# {RoutesParser}. The descriptor's auto-collected
|
|
68
95
|
# `FileEntry` digest invalidates the cache on routes-
|
|
69
96
|
# file edits.
|
|
97
|
+
#
|
|
98
|
+
# Passes a `file_reader` lambda so the parser can follow
|
|
99
|
+
# `draw(:admin)` → `config/routes/admin.rb` partials.
|
|
70
100
|
producer :helper_table do |_params|
|
|
101
|
+
routes_dir = "#{File.dirname(@routes_file)}/routes"
|
|
102
|
+
file_reader = lambda do |name|
|
|
103
|
+
io_boundary.read_file("#{routes_dir}/#{name}")
|
|
104
|
+
rescue StandardError
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
71
107
|
contents = io_boundary.read_file(@routes_file)
|
|
72
|
-
|
|
108
|
+
custom_helpers = discover_custom_helpers
|
|
109
|
+
RoutesParser.parse(contents, file_reader: file_reader, custom_helpers: custom_helpers)
|
|
73
110
|
end
|
|
74
111
|
|
|
75
112
|
def init(_services)
|
|
76
113
|
@routes_file = config.fetch("routes_file", DEFAULT_ROUTES_FILE)
|
|
114
|
+
@helper_paths = Array(config.fetch("helper_paths", DEFAULT_HELPER_PATHS)).map(&:to_s)
|
|
77
115
|
@helper_table = nil
|
|
78
116
|
@load_error = nil
|
|
79
117
|
end
|
|
80
118
|
|
|
119
|
+
# Walks every configured `helper_paths:` directory
|
|
120
|
+
# through the trusted `IoBoundary` and returns the set
|
|
121
|
+
# of project-defined `*_path` / `*_url` method names
|
|
122
|
+
# for {HelperDiscoverer}. Each file digest is captured
|
|
123
|
+
# by the boundary so editing a helper file invalidates
|
|
124
|
+
# the `:helper_table` cache automatically. Returns the
|
|
125
|
+
# empty set when nothing under `helper_paths:` exists —
|
|
126
|
+
# the routes table still works.
|
|
127
|
+
def discover_custom_helpers
|
|
128
|
+
contents_per_path = {}
|
|
129
|
+
each_helper_file do |path|
|
|
130
|
+
contents_per_path[path] = io_boundary.read_file(path)
|
|
131
|
+
rescue Plugin::AccessDeniedError, Errno::ENOENT
|
|
132
|
+
next
|
|
133
|
+
end
|
|
134
|
+
HelperDiscoverer.discover(contents_per_path)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def pre_read_helper_files
|
|
138
|
+
each_helper_file do |path|
|
|
139
|
+
io_boundary.read_file(path)
|
|
140
|
+
rescue Plugin::AccessDeniedError, Errno::ENOENT
|
|
141
|
+
next
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def each_helper_file(&)
|
|
146
|
+
@helper_paths.each do |dir|
|
|
147
|
+
absolute = File.expand_path(dir)
|
|
148
|
+
next unless File.directory?(absolute)
|
|
149
|
+
|
|
150
|
+
Dir.glob(File.join(absolute, "**", "*.rb")).each(&)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
81
154
|
# Publishes the parsed table to the cross-plugin fact
|
|
82
155
|
# store so future Tier 2 plugins (rigor-actionpack
|
|
83
156
|
# Phase 4) can read it via `services.fact_store.read`.
|
|
@@ -122,8 +195,12 @@ module Rigor
|
|
|
122
195
|
# Read first so the IoBoundary's FileEntry digest
|
|
123
196
|
# captures into the descriptor before `cache_for`
|
|
124
197
|
# snapshots it (the same pattern documented in
|
|
125
|
-
# rigor-routes / rigor-activerecord).
|
|
198
|
+
# rigor-routes / rigor-activerecord). Helper files are
|
|
199
|
+
# pre-read for the same reason — editing a file under
|
|
200
|
+
# `app/helpers/` MUST invalidate the helper_table cache
|
|
201
|
+
# so the new custom-helper set is picked up.
|
|
126
202
|
io_boundary.read_file(@routes_file)
|
|
203
|
+
pre_read_helper_files
|
|
127
204
|
@helper_table = cache_for(:helper_table, params: {}).call
|
|
128
205
|
rescue Plugin::AccessDeniedError => e
|
|
129
206
|
@load_error = "rigor-rails-routes: #{e.message}"
|
data/sig/rigor/scope.rbs
CHANGED
|
@@ -18,8 +18,22 @@ module Rigor
|
|
|
18
18
|
attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]]
|
|
19
19
|
attr_reader discovered_superclasses: Hash[String, String]
|
|
20
20
|
attr_reader discovered_includes: Hash[String, Array[String]]
|
|
21
|
+
attr_reader indexed_narrowings: Hash[IndexedKey, Type::t]
|
|
22
|
+
attr_reader method_chain_narrowings: Hash[ChainKey, Type::t]
|
|
21
23
|
attr_reader source_path: String?
|
|
22
24
|
|
|
25
|
+
class IndexedKey
|
|
26
|
+
attr_reader receiver_kind: Symbol
|
|
27
|
+
attr_reader receiver_name: Symbol
|
|
28
|
+
attr_reader key: untyped
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class ChainKey
|
|
32
|
+
attr_reader receiver_kind: Symbol
|
|
33
|
+
attr_reader receiver_name: Symbol
|
|
34
|
+
attr_reader method_name: Symbol
|
|
35
|
+
end
|
|
36
|
+
|
|
23
37
|
def self.empty: (?environment: Environment, ?source_path: String?) -> Scope
|
|
24
38
|
|
|
25
39
|
def initialize: (environment: Environment, locals: Hash[Symbol, Type::t], ?fact_store: Analysis::FactStore, ?self_type: Type::t?, ?declared_types: Hash[untyped, Type::t], ?ivars: Hash[Symbol, Type::t], ?cvars: Hash[Symbol, Type::t], ?globals: Hash[Symbol, Type::t], ?source_path: String?) -> void
|
|
@@ -50,6 +64,14 @@ module Rigor
|
|
|
50
64
|
def with_discovered_superclasses: (Hash[String, String] table) -> Scope
|
|
51
65
|
def includes_of: (String | Symbol class_name) -> Array[String]
|
|
52
66
|
def with_discovered_includes: (Hash[String, Array[String]] table) -> Scope
|
|
67
|
+
def indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key) -> Type::t?
|
|
68
|
+
def with_indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key, Type::t type) -> Scope
|
|
69
|
+
def without_indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key) -> Scope
|
|
70
|
+
def without_indexed_narrowings_for: (Symbol receiver_kind, String | Symbol receiver_name) -> Scope
|
|
71
|
+
def method_chain_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, String | Symbol method_name) -> Type::t?
|
|
72
|
+
def with_method_chain_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, String | Symbol method_name, Type::t type) -> Scope
|
|
73
|
+
def without_method_chain_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, String | Symbol method_name) -> Scope
|
|
74
|
+
def without_method_chain_narrowings_for: (Symbol receiver_kind, String | Symbol receiver_name) -> Scope
|
|
53
75
|
def with_fact: (Analysis::FactStore::Fact fact) -> Scope
|
|
54
76
|
def with_self_type: (Type::t? type) -> Scope
|
|
55
77
|
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.
|
|
4
|
+
version: 0.1.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rigor contributors
|
|
@@ -257,6 +257,7 @@ files:
|
|
|
257
257
|
- lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb
|
|
258
258
|
- lib/rigor/analysis/dependency_source_inference/walker.rb
|
|
259
259
|
- lib/rigor/analysis/diagnostic.rb
|
|
260
|
+
- lib/rigor/analysis/erb_template_detector.rb
|
|
260
261
|
- lib/rigor/analysis/fact_store.rb
|
|
261
262
|
- lib/rigor/analysis/project_scan.rb
|
|
262
263
|
- lib/rigor/analysis/result.rb
|
|
@@ -290,6 +291,8 @@ files:
|
|
|
290
291
|
- lib/rigor/cli/explain_command.rb
|
|
291
292
|
- lib/rigor/cli/lsp_command.rb
|
|
292
293
|
- lib/rigor/cli/mcp_command.rb
|
|
294
|
+
- lib/rigor/cli/plugins_command.rb
|
|
295
|
+
- lib/rigor/cli/plugins_renderer.rb
|
|
293
296
|
- lib/rigor/cli/prism_colorizer.rb
|
|
294
297
|
- lib/rigor/cli/sig_gen_command.rb
|
|
295
298
|
- lib/rigor/cli/triage_command.rb
|
|
@@ -351,6 +354,7 @@ files:
|
|
|
351
354
|
- lib/rigor/inference/hkt_body_parser.rb
|
|
352
355
|
- lib/rigor/inference/hkt_reducer.rb
|
|
353
356
|
- lib/rigor/inference/hkt_registry.rb
|
|
357
|
+
- lib/rigor/inference/indexed_narrowing.rb
|
|
354
358
|
- lib/rigor/inference/macro_block_self_type.rb
|
|
355
359
|
- lib/rigor/inference/method_dispatcher.rb
|
|
356
360
|
- lib/rigor/inference/method_dispatcher/block_folding.rb
|
|
@@ -373,6 +377,7 @@ files:
|
|
|
373
377
|
- lib/rigor/inference/method_dispatcher/uri_folding.rb
|
|
374
378
|
- lib/rigor/inference/method_parameter_binder.rb
|
|
375
379
|
- lib/rigor/inference/multi_target_binder.rb
|
|
380
|
+
- lib/rigor/inference/mutation_widening.rb
|
|
376
381
|
- lib/rigor/inference/narrowing.rb
|
|
377
382
|
- lib/rigor/inference/precision_scanner.rb
|
|
378
383
|
- lib/rigor/inference/project_patched_methods.rb
|
|
@@ -555,6 +560,9 @@ files:
|
|
|
555
560
|
- plugins/rigor-rails-routes/lib/rigor-rails-routes.rb
|
|
556
561
|
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb
|
|
557
562
|
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb
|
|
563
|
+
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb
|
|
564
|
+
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb
|
|
565
|
+
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb
|
|
558
566
|
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb
|
|
559
567
|
- plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb
|
|
560
568
|
- plugins/rigor-rails/lib/rigor-rails.rb
|