rigortype 0.1.15 → 0.1.16
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 +4 -2
- data/exe/rigor +19 -0
- data/lib/rigor/analysis/check_rules.rb +25 -1
- data/lib/rigor/analysis/diagnostic.rb +40 -0
- data/lib/rigor/analysis/runner.rb +61 -2
- data/lib/rigor/analysis/worker_session.rb +3 -2
- data/lib/rigor/cache/descriptor.rb +6 -2
- data/lib/rigor/cli/plugins_command.rb +51 -4
- data/lib/rigor/cli/plugins_renderer.rb +86 -1
- data/lib/rigor/cli.rb +135 -5
- data/lib/rigor/environment/rbs_loader.rb +259 -1
- data/lib/rigor/environment.rb +8 -2
- data/lib/rigor/inference/budget_trace.rb +137 -0
- data/lib/rigor/inference/expression_typer.rb +9 -2
- data/lib/rigor/inference/hkt_reducer.rb +2 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +23 -6
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +81 -14
- data/lib/rigor/inference/method_dispatcher.rb +57 -10
- data/lib/rigor/inference/precision_scanner.rb +60 -1
- data/lib/rigor/inference/scope_indexer.rb +127 -8
- data/lib/rigor/inference/statement_evaluator.rb +13 -8
- data/lib/rigor/inference/synthetic_method_index.rb +23 -4
- data/lib/rigor/inference/synthetic_method_scanner.rb +148 -14
- data/lib/rigor/plugin/additional_initializer.rb +108 -0
- data/lib/rigor/plugin/base.rb +321 -2
- data/lib/rigor/plugin/box.rb +64 -0
- data/lib/rigor/plugin/inflector.rb +121 -0
- data/lib/rigor/plugin/isolation.rb +191 -0
- data/lib/rigor/plugin/macro/nested_class_template.rb +140 -0
- data/lib/rigor/plugin/macro.rb +1 -0
- data/lib/rigor/plugin/manifest.rb +120 -23
- data/lib/rigor/plugin/node_context.rb +62 -0
- data/lib/rigor/plugin/registry.rb +10 -0
- data/lib/rigor/plugin.rb +3 -0
- data/lib/rigor/sig_gen/generator.rb +2 -3
- data/lib/rigor/sig_gen/observation_collector.rb +2 -2
- data/lib/rigor/source/literals.rb +118 -0
- data/lib/rigor/source/node_walker.rb +26 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +6 -1
- data/lib/rigor/type/union.rb +65 -1
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +31 -53
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +21 -23
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +38 -59
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +7 -13
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +22 -33
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +298 -413
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +69 -71
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +24 -34
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +18 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +4 -46
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +1 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +17 -12
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -8
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +2 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +2 -6
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +4 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +40 -45
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +7 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +20 -42
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +7 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +4 -8
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +188 -0
- data/plugins/rigor-mangrove/lib/rigor-mangrove.rb +3 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +4 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +24 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +31 -48
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +21 -23
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +54 -82
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +25 -25
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +63 -147
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -17
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +23 -114
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +36 -31
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +6 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +4 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +13 -12
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +28 -40
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +44 -47
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +11 -10
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +45 -87
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +11 -12
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +29 -42
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +20 -19
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +73 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +43 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +36 -96
- data/sig/rigor/plugin/access_denied_error.rbs +3 -1
- data/sig/rigor/plugin/base.rbs +58 -3
- data/sig/rigor/plugin/io_boundary.rbs +3 -0
- data/sig/rigor/plugin/manifest.rbs +31 -1
- data/sig/rigor/source.rbs +12 -0
- data/sig/rigor.rbs +5 -0
- data/skills/rigor-plugin-author/SKILL.md +13 -9
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +6 -5
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +159 -75
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +3 -3
- metadata +52 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/inflector.rb +0 -114
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "prism"
|
|
4
|
+
require "rigor/source/literals"
|
|
4
5
|
|
|
5
6
|
require_relative "mailer_index"
|
|
6
7
|
|
|
@@ -264,7 +265,7 @@ module Rigor
|
|
|
264
265
|
next unless node.is_a?(Prism::CallNode) && node.receiver.nil?
|
|
265
266
|
|
|
266
267
|
args = (node.arguments&.arguments || []).filter_map do |arg|
|
|
267
|
-
|
|
268
|
+
Rigor::Source::Literals.symbol(arg)
|
|
268
269
|
end
|
|
269
270
|
next if args.empty?
|
|
270
271
|
|
|
@@ -371,7 +372,11 @@ module Rigor
|
|
|
371
372
|
# denied) are swallowed.
|
|
372
373
|
def view_exists?(class_name, action_name)
|
|
373
374
|
views_root_absolute = File.expand_path(@views_root)
|
|
374
|
-
|
|
375
|
+
# ADR-39: the real ActiveSupport::Inflector resolves the mailer's
|
|
376
|
+
# view directory (`Foo::BarMailer` → `foo/bar_mailer`), so a
|
|
377
|
+
# divergence from Rails' real underscore can't point the
|
|
378
|
+
# missing-view check at the wrong directory.
|
|
379
|
+
underscore_path = Rigor::Plugin::Inflector.underscore(class_name.delete_prefix("::"))
|
|
375
380
|
mailer_dir = File.join(views_root_absolute, underscore_path)
|
|
376
381
|
|
|
377
382
|
VIEW_FORMATS.any? do |format|
|
|
@@ -381,17 +386,6 @@ module Rigor
|
|
|
381
386
|
end
|
|
382
387
|
end
|
|
383
388
|
end
|
|
384
|
-
|
|
385
|
-
# Convert `Foo::BarMailer` → `foo/bar_mailer`. Mirrors
|
|
386
|
-
# ActiveSupport's String#underscore for ASCII-only
|
|
387
|
-
# constant names; we don't try to be inflector-perfect
|
|
388
|
-
# here.
|
|
389
|
-
def underscore(name)
|
|
390
|
-
name.gsub("::", "/")
|
|
391
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
392
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
393
|
-
.downcase
|
|
394
|
-
end
|
|
395
389
|
end
|
|
396
390
|
end
|
|
397
391
|
end
|
|
@@ -59,16 +59,12 @@ module Rigor
|
|
|
59
59
|
version: "0.4.0",
|
|
60
60
|
description: "Validates ActionMailer call shape and view template existence.",
|
|
61
61
|
config_schema: {
|
|
62
|
-
"mailer_search_paths" => :array,
|
|
63
|
-
"mailer_base_classes" => :array,
|
|
64
|
-
"views_root" => :string
|
|
62
|
+
"mailer_search_paths" => { kind: :array, default: ["app/mailers"] },
|
|
63
|
+
"mailer_base_classes" => { kind: :array, default: %w[ApplicationMailer ActionMailer::Base] },
|
|
64
|
+
"views_root" => { kind: :string, default: "app/views" }
|
|
65
65
|
}
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
-
DEFAULT_MAILER_SEARCH_PATHS = ["app/mailers"].freeze
|
|
69
|
-
DEFAULT_MAILER_BASE_CLASSES = %w[ApplicationMailer ActionMailer::Base].freeze
|
|
70
|
-
DEFAULT_VIEWS_ROOT = "app/views"
|
|
71
|
-
|
|
72
68
|
producer :mailer_index do |_params|
|
|
73
69
|
MailerDiscoverer.new(
|
|
74
70
|
io_boundary: io_boundary,
|
|
@@ -79,22 +75,33 @@ module Rigor
|
|
|
79
75
|
end
|
|
80
76
|
|
|
81
77
|
def init(_services)
|
|
82
|
-
@mailer_search_paths = Array(config.fetch("mailer_search_paths"
|
|
83
|
-
@mailer_base_classes = Array(config.fetch("mailer_base_classes"
|
|
84
|
-
@views_root = config.fetch("views_root"
|
|
78
|
+
@mailer_search_paths = Array(config.fetch("mailer_search_paths")).map(&:to_s)
|
|
79
|
+
@mailer_base_classes = Array(config.fetch("mailer_base_classes")).map(&:to_s)
|
|
80
|
+
@views_root = config.fetch("views_root").to_s
|
|
85
81
|
@mailer_index = nil
|
|
86
82
|
@load_error = nil
|
|
87
83
|
end
|
|
88
84
|
|
|
85
|
+
# File-level: load-error + the missing-view check (anchored on the
|
|
86
|
+
# mailer's own source file, so it is genuinely per-file, not
|
|
87
|
+
# per-call). The per-call mailer validation (action existence /
|
|
88
|
+
# arity) runs over the engine-owned walk via the node_rule below
|
|
89
|
+
# (ADR-37). The mailer index is lazily loaded + memoised, shared.
|
|
89
90
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
90
91
|
index = mailer_index_or_nil
|
|
91
92
|
return [load_error_diagnostic(path)] if index.nil? && @load_error
|
|
92
93
|
return [] if index.nil? || index.empty?
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
missing_view_diagnostics(path, index)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
node_rule Prism::CallNode do |node, _scope, path|
|
|
99
|
+
index = mailer_index_or_nil
|
|
100
|
+
next [] if index.nil? || index.empty?
|
|
101
|
+
|
|
102
|
+
Analyzer.violations_for(call_node: node, mailer_index: index).map do |violation|
|
|
103
|
+
diagnostic(node, path: path, message: violation.message, severity: violation.severity, rule: violation.rule)
|
|
104
|
+
end
|
|
98
105
|
end
|
|
99
106
|
|
|
100
107
|
private
|
|
@@ -121,10 +128,6 @@ module Rigor
|
|
|
121
128
|
nil
|
|
122
129
|
end
|
|
123
130
|
|
|
124
|
-
def call_site_diagnostics(path, root, index)
|
|
125
|
-
Analyzer.diagnose(path: path, root: root, mailer_index: index).map { |diag| build_diagnostic(diag) }
|
|
126
|
-
end
|
|
127
|
-
|
|
128
131
|
# Anchors `missing-view` diagnostics on the mailer file
|
|
129
132
|
# itself: when the file currently being analysed is the
|
|
130
133
|
# mailer's source file, emit one diagnostic per missing
|
|
@@ -143,7 +146,7 @@ module Rigor
|
|
|
143
146
|
severity: :warning,
|
|
144
147
|
rule: "missing-view",
|
|
145
148
|
message: "`#{class_entry.class_name}##{action_name}` has no view template " \
|
|
146
|
-
"under `#{@views_root}/#{underscore(class_entry.class_name.delete_prefix('::'))}/`"
|
|
149
|
+
"under `#{@views_root}/#{Rigor::Plugin::Inflector.underscore(class_entry.class_name.delete_prefix('::'))}/`"
|
|
147
150
|
)
|
|
148
151
|
end
|
|
149
152
|
end
|
|
@@ -154,13 +157,6 @@ module Rigor
|
|
|
154
157
|
File.expand_path(path)
|
|
155
158
|
end
|
|
156
159
|
|
|
157
|
-
def underscore(name)
|
|
158
|
-
name.gsub("::", "/")
|
|
159
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
160
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
161
|
-
.downcase
|
|
162
|
-
end
|
|
163
|
-
|
|
164
160
|
def load_error_diagnostic(path)
|
|
165
161
|
Rigor::Analysis::Diagnostic.new(
|
|
166
162
|
path: path, line: 1, column: 1,
|
|
@@ -169,13 +165,6 @@ module Rigor
|
|
|
169
165
|
rule: "load-error"
|
|
170
166
|
)
|
|
171
167
|
end
|
|
172
|
-
|
|
173
|
-
def build_diagnostic(diag)
|
|
174
|
-
Rigor::Analysis::Diagnostic.new(
|
|
175
|
-
path: diag.path, line: diag.line, column: diag.column,
|
|
176
|
-
message: diag.message, severity: diag.severity, rule: diag.rule
|
|
177
|
-
)
|
|
178
|
-
end
|
|
179
168
|
end
|
|
180
169
|
|
|
181
170
|
Rigor::Plugin.register(Actionmailer)
|