rigortype 0.1.18 → 0.1.19
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 +159 -224
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +25 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +29 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +54 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +169 -23
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules.rb +266 -63
- data/lib/rigor/analysis/diagnostic.rb +8 -0
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +2 -1
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -1
- data/lib/rigor/analysis/runner.rb +58 -21
- data/lib/rigor/analysis/worker_session.rb +21 -11
- data/lib/rigor/bleeding_edge.rb +123 -0
- data/lib/rigor/cache/descriptor.rb +86 -8
- data/lib/rigor/cache/rbs_descriptor.rb +2 -1
- data/lib/rigor/cli/annotate_command.rb +100 -15
- data/lib/rigor/cli/check_command.rb +3 -0
- data/lib/rigor/cli/plugins_command.rb +2 -4
- data/lib/rigor/cli/plugins_renderer.rb +0 -2
- data/lib/rigor/cli/show_bleedingedge_command.rb +114 -0
- data/lib/rigor/cli/triage_command.rb +6 -3
- data/lib/rigor/cli/triage_renderer.rb +15 -1
- data/lib/rigor/cli.rb +9 -1
- data/lib/rigor/configuration/severity_profile.rb +13 -1
- data/lib/rigor/configuration.rb +57 -1
- data/lib/rigor/environment/rbs_loader.rb +25 -0
- data/lib/rigor/inference/body_fixpoint.rb +89 -0
- data/lib/rigor/inference/budget_trace.rb +29 -2
- data/lib/rigor/inference/expression_typer.rb +1052 -43
- data/lib/rigor/inference/macro_block_self_type.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +60 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +54 -14
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +281 -0
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +71 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +148 -10
- data/lib/rigor/inference/method_dispatcher.rb +72 -1
- data/lib/rigor/inference/method_parameter_binder.rb +56 -2
- data/lib/rigor/inference/multi_target_binder.rb +46 -3
- data/lib/rigor/inference/mutation_widening.rb +142 -0
- data/lib/rigor/inference/narrowing.rb +270 -37
- data/lib/rigor/inference/scope_indexer.rb +696 -25
- data/lib/rigor/inference/statement_evaluator.rb +963 -16
- data/lib/rigor/inference/synthetic_method_scanner.rb +1 -1
- data/lib/rigor/plugin/base.rb +235 -79
- data/lib/rigor/plugin/macro/block_as_method.rb +22 -21
- data/lib/rigor/plugin/macro/nested_class_template.rb +9 -7
- data/lib/rigor/plugin/macro.rb +2 -3
- data/lib/rigor/plugin/manifest.rb +4 -24
- data/lib/rigor/plugin/node_rule_walk.rb +59 -14
- data/lib/rigor/plugin/registry.rb +12 -11
- data/lib/rigor/scope/discovery_index.rb +2 -0
- data/lib/rigor/scope.rb +132 -6
- data/lib/rigor/sig_gen/generator.rb +8 -0
- data/lib/rigor/triage/catalogue.rb +4 -19
- data/lib/rigor/triage.rb +69 -1
- data/lib/rigor/type/combinator.rb +29 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +13 -29
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -32
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +27 -90
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +13 -30
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +20 -19
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +10 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +11 -40
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +1 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +10 -21
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +21 -34
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +11 -18
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -13
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +3 -23
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +8 -21
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +25 -0
- data/sig/rigor/analysis/fact_store.rbs +3 -0
- data/sig/rigor/inference/builtins/method_catalog.rbs +1 -1
- data/sig/rigor/plugin/base.rbs +5 -2
- data/sig/rigor/plugin/manifest.rbs +1 -2
- data/sig/rigor/scope.rbs +10 -1
- data/sig/rigor/type.rbs +1 -0
- data/sig/rigor.rbs +1 -1
- data/skills/rigor-baseline-reduce/references/01-classify.md +27 -0
- data/skills/rigor-plugin-author/SKILL.md +6 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +22 -17
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +18 -1
- metadata +7 -2
- data/lib/rigor/plugin/macro/external_file.rb +0 -143
|
@@ -37,12 +37,15 @@ rows the rule positions with `diagnostic` (the bundled plugins follow
|
|
|
37
37
|
this `Analyzer.violations_for` split, which also makes the logic
|
|
38
38
|
unit-testable without running the whole engine).
|
|
39
39
|
|
|
40
|
-
>
|
|
41
|
-
>
|
|
42
|
-
>
|
|
43
|
-
>
|
|
44
|
-
>
|
|
45
|
-
>
|
|
40
|
+
> `diagnostics_for_file(path:, scope:, root:)` is the **file-rule**
|
|
41
|
+
> surface — use it for a genuinely file-scoped result (a single
|
|
42
|
+
> load-error row, a cross-file aggregation, or a check that needs the
|
|
43
|
+
> whole parsed file at once). It is not deprecated; it is the right tool
|
|
44
|
+
> when a check isn't per-node. Per-node checks use `node_rule` (you don't
|
|
45
|
+
> hand-roll a `def walk` / `compact_child_nodes.each` recursion — the
|
|
46
|
+
> engine owns the walk). Map a violation array to diagnostics with
|
|
47
|
+
> `diagnostics_for(violations, path:, node:)` rather than a hand-rolled
|
|
48
|
+
> `.map { diagnostic(...) }`.
|
|
46
49
|
|
|
47
50
|
### Recognising call sites
|
|
48
51
|
|
|
@@ -59,7 +62,9 @@ re-deriving the `unescaped.to_sym` shape.
|
|
|
59
62
|
validate references): declare `node_file_context { |root, scope| … }`.
|
|
60
63
|
It runs once per file before the walk and its return value is threaded
|
|
61
64
|
to every rule as `file_context`. (A *cross-file* collect belongs in
|
|
62
|
-
`#prepare` + `services.fact_store
|
|
65
|
+
`#prepare` + `services.fact_store.publish`; the consuming plugin reads
|
|
66
|
+
it with `read_fact(plugin_id:, name:)` — the nil-inclusive memo means
|
|
67
|
+
you never hand-roll an `@x_resolved` flag. See
|
|
63
68
|
[`01-plan-and-scaffold.md`](01-plan-and-scaffold.md).)
|
|
64
69
|
- **Where the node sits**: `context` (a `Rigor::Plugin::NodeContext`)
|
|
65
70
|
carries the lexical ancestor chain — `context.enclosing_def`,
|
|
@@ -213,16 +218,16 @@ plugin is confident; a wrong contribution propagates downstream.
|
|
|
213
218
|
`rigor plugins --capabilities` catalogue enumerates — run it to see
|
|
214
219
|
exactly what each loaded plugin contributes.
|
|
215
220
|
|
|
216
|
-
> **The
|
|
217
|
-
>
|
|
218
|
-
> `
|
|
219
|
-
>
|
|
220
|
-
>
|
|
221
|
-
>
|
|
222
|
-
>
|
|
223
|
-
> (ActiveStorage's `Attached::One`
|
|
224
|
-
>
|
|
225
|
-
>
|
|
221
|
+
> **The removed fat hook.** The original `flow_contribution_for(call_node:,
|
|
222
|
+
> scope:)` was **deleted pre-1.0 in ADR-52 WD3** — defining it now raises
|
|
223
|
+
> `ArgumentError` at load time. The two shapes it used to cover are
|
|
224
|
+
> expressed by `dynamic_return`'s callable gates: a **method-gated return
|
|
225
|
+
> type** (an RSpec `let(:x) { … }` binding, a Sorbet `sig`-driven return —
|
|
226
|
+
> keyed on the method, not a fixed receiver class) uses
|
|
227
|
+
> `dynamic_return methods: -> { [...] }` or `file_methods: ->(path) { [...] }`;
|
|
228
|
+
> a **dynamic per-project receiver set** (ActiveStorage's `Attached::One`
|
|
229
|
+
> on discovered model classes) uses `dynamic_return receivers: -> { [...] }`,
|
|
230
|
+
> the callable resolved once after `#prepare`. These return-type surfaces
|
|
226
231
|
> are the most contract-sensitive part of the API — implement one only
|
|
227
232
|
> if the plugin genuinely needs to sharpen call-site types; a
|
|
228
233
|
> diagnostics-only plugin skips them entirely.
|
|
@@ -23,6 +23,8 @@ The JSON shape:
|
|
|
23
23
|
{
|
|
24
24
|
"summary": { "total": 489, "error": 480, "warning": 9, "info": 0 },
|
|
25
25
|
"distribution": [ { "rule": "call.undefined-method", "count": 437 } ],
|
|
26
|
+
"selectors": [ { "receiver": "String", "method": "squish", "count": 31,
|
|
27
|
+
"files": 12, "rules": { "call.undefined-method": 31 } } ],
|
|
26
28
|
"hotspots": [ { "file": "app/models/status.rb", "count": 42,
|
|
27
29
|
"by_rule": { "call.undefined-method": 40 } } ],
|
|
28
30
|
"hints": [
|
|
@@ -32,11 +34,26 @@ The JSON shape:
|
|
|
32
34
|
}
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
Use the
|
|
37
|
+
Use the sections like this:
|
|
36
38
|
|
|
37
39
|
- **`summary` / `distribution`** — the scale, and which rules
|
|
38
40
|
dominate. Decides nothing on its own; feeds the mode sanity-check
|
|
39
41
|
(>100 errors → acknowledge mode is the right default).
|
|
42
|
+
- **`selectors`** — the by-(class, method) axis. Each row is a
|
|
43
|
+
dispatch target (`String#squish`) with its `count`, the `files` it
|
|
44
|
+
spans, and the `rules` that fired. Read it with `jq` to find the
|
|
45
|
+
*shape* of the problem before touching code — these are structured
|
|
46
|
+
fields, never parse the `message`:
|
|
47
|
+
```sh
|
|
48
|
+
# the 10 methods responsible for the most diagnostics
|
|
49
|
+
rigor triage --format json | jq -r '.selectors[:10][] | "\(.count)\t\(.receiver)#\(.method)"'
|
|
50
|
+
# methods missing on the same receiver across many files = one config
|
|
51
|
+
# gap (an unloaded core-ext / unseen monkey-patch), not many bugs
|
|
52
|
+
rigor triage --format json | jq '.selectors[] | select(.files >= 5)'
|
|
53
|
+
```
|
|
54
|
+
A high `count` + high `files` selector is almost always a *systemic
|
|
55
|
+
cause* (a plugin / `pre_eval:` fix clears it in bulk); a low `count`
|
|
56
|
+
selector is a candidate genuine bug.
|
|
40
57
|
- **`hotspots`** — files carrying the most diagnostics. A single hot
|
|
41
58
|
file is often one structural cause, not many bugs.
|
|
42
59
|
- **`hints`** — the heuristic catalogue. Each hint names a *likely
|
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.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rigor contributors
|
|
@@ -289,6 +289,7 @@ files:
|
|
|
289
289
|
- lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb
|
|
290
290
|
- lib/rigor/analysis/check_rules/dead_assignment_collector.rb
|
|
291
291
|
- lib/rigor/analysis/check_rules/ivar_write_collector.rb
|
|
292
|
+
- lib/rigor/analysis/check_rules/main_pass_collector.rb
|
|
292
293
|
- lib/rigor/analysis/check_rules/rule_walk.rb
|
|
293
294
|
- lib/rigor/analysis/check_rules/self_closedness_scanner.rb
|
|
294
295
|
- lib/rigor/analysis/check_rules/unreachable_clause_collector.rb
|
|
@@ -318,6 +319,7 @@ files:
|
|
|
318
319
|
- lib/rigor/analysis/worker_session.rb
|
|
319
320
|
- lib/rigor/ast.rb
|
|
320
321
|
- lib/rigor/ast/type_node.rb
|
|
322
|
+
- lib/rigor/bleeding_edge.rb
|
|
321
323
|
- lib/rigor/builtins/hkt_builtins.rb
|
|
322
324
|
- lib/rigor/builtins/imported_refinements.rb
|
|
323
325
|
- lib/rigor/builtins/regex_refinement.rb
|
|
@@ -353,6 +355,7 @@ files:
|
|
|
353
355
|
- lib/rigor/cli/plugins_renderer.rb
|
|
354
356
|
- lib/rigor/cli/prism_colorizer.rb
|
|
355
357
|
- lib/rigor/cli/renderable.rb
|
|
358
|
+
- lib/rigor/cli/show_bleedingedge_command.rb
|
|
356
359
|
- lib/rigor/cli/sig_gen_command.rb
|
|
357
360
|
- lib/rigor/cli/skill_command.rb
|
|
358
361
|
- lib/rigor/cli/trace_command.rb
|
|
@@ -386,6 +389,7 @@ files:
|
|
|
386
389
|
- lib/rigor/flow_contribution/merger.rb
|
|
387
390
|
- lib/rigor/inference/acceptance.rb
|
|
388
391
|
- lib/rigor/inference/block_parameter_binder.rb
|
|
392
|
+
- lib/rigor/inference/body_fixpoint.rb
|
|
389
393
|
- lib/rigor/inference/budget_trace.rb
|
|
390
394
|
- lib/rigor/inference/builtins/array_catalog.rb
|
|
391
395
|
- lib/rigor/inference/builtins/comparable_catalog.rb
|
|
@@ -421,6 +425,7 @@ files:
|
|
|
421
425
|
- lib/rigor/inference/indexed_narrowing.rb
|
|
422
426
|
- lib/rigor/inference/macro_block_self_type.rb
|
|
423
427
|
- lib/rigor/inference/method_dispatcher.rb
|
|
428
|
+
- lib/rigor/inference/method_dispatcher/array_to_h_folding.rb
|
|
424
429
|
- lib/rigor/inference/method_dispatcher/block_folding.rb
|
|
425
430
|
- lib/rigor/inference/method_dispatcher/call_context.rb
|
|
426
431
|
- lib/rigor/inference/method_dispatcher/cgi_folding.rb
|
|
@@ -435,6 +440,7 @@ files:
|
|
|
435
440
|
- lib/rigor/inference/method_dispatcher/overload_selector.rb
|
|
436
441
|
- lib/rigor/inference/method_dispatcher/rbs_dispatch.rb
|
|
437
442
|
- lib/rigor/inference/method_dispatcher/receiver_affinity.rb
|
|
443
|
+
- lib/rigor/inference/method_dispatcher/reduce_folding.rb
|
|
438
444
|
- lib/rigor/inference/method_dispatcher/regexp_folding.rb
|
|
439
445
|
- lib/rigor/inference/method_dispatcher/set_folding.rb
|
|
440
446
|
- lib/rigor/inference/method_dispatcher/shape_dispatch.rb
|
|
@@ -489,7 +495,6 @@ files:
|
|
|
489
495
|
- lib/rigor/plugin/loader.rb
|
|
490
496
|
- lib/rigor/plugin/macro.rb
|
|
491
497
|
- lib/rigor/plugin/macro/block_as_method.rb
|
|
492
|
-
- lib/rigor/plugin/macro/external_file.rb
|
|
493
498
|
- lib/rigor/plugin/macro/heredoc_template.rb
|
|
494
499
|
- lib/rigor/plugin/macro/nested_class_template.rb
|
|
495
500
|
- lib/rigor/plugin/macro/trait_registry.rb
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rigor
|
|
4
|
-
module Plugin
|
|
5
|
-
module Macro
|
|
6
|
-
# ADR-16 Tier D declaration: "files matching `glob` are
|
|
7
|
-
# analysed as if their body were pasted at a call site whose
|
|
8
|
-
# `self` is an instance of `receiver_type` (and whose `@ivar`
|
|
9
|
-
# facts come from `bound_ivars`)."
|
|
10
|
-
#
|
|
11
|
-
# Worked motivating cases (per the per-library survey):
|
|
12
|
-
#
|
|
13
|
-
# - Redmine's `WebhookPayload#instance_eval(File.read(path), path, 1)`
|
|
14
|
-
# at `app/models/webhook_payload.rb:71`. The payload templates
|
|
15
|
-
# under `config/webhooks/*.rb` run with `self` typed as
|
|
16
|
-
# `Redmine::WebhookPayload` and ivars like `@event` / `@issue`
|
|
17
|
-
# / `@user` pre-bound by the caller.
|
|
18
|
-
# - tDiary Core's plugin loader pattern — `misc/plugin/*.rb`
|
|
19
|
-
# files loaded under `instance_eval` with the tDiary plugin
|
|
20
|
-
# instance as `self`.
|
|
21
|
-
#
|
|
22
|
-
# ## Authoring shape
|
|
23
|
-
#
|
|
24
|
-
# manifest(
|
|
25
|
-
# id: "redmine-webhook-payloads",
|
|
26
|
-
# version: "0.1.0",
|
|
27
|
-
# external_files: [
|
|
28
|
-
# Rigor::Plugin::Macro::ExternalFile.new(
|
|
29
|
-
# glob: "config/webhooks/*.rb",
|
|
30
|
-
# receiver_type: "Redmine::WebhookPayload",
|
|
31
|
-
# bound_ivars: {
|
|
32
|
-
# "@event" => "Symbol",
|
|
33
|
-
# "@issue" => "Issue?",
|
|
34
|
-
# "@user" => "User"
|
|
35
|
-
# }
|
|
36
|
-
# )
|
|
37
|
-
# ]
|
|
38
|
-
# )
|
|
39
|
-
#
|
|
40
|
-
# ## Fields
|
|
41
|
-
#
|
|
42
|
-
# - `glob` — non-empty String pattern. Interpreted relative
|
|
43
|
-
# to the project root (the directory containing `.rigor.yml`)
|
|
44
|
-
# at scan time. Slice 5a accepts any non-empty glob
|
|
45
|
-
# pattern syntactically; the engine integration (slice 5b)
|
|
46
|
-
# pins the resolution rule.
|
|
47
|
-
# - `receiver_type` — non-empty String. The class name `self`
|
|
48
|
-
# inside the loaded file binds to. Engine integration (slice
|
|
49
|
-
# 5b) narrows the file-entry scope's `self_type` to
|
|
50
|
-
# `Nominal[receiver_type]`.
|
|
51
|
-
# - `bound_ivars` — Hash<String, String>. Each key MUST start
|
|
52
|
-
# with `@`; each value is a non-empty type-name String. The
|
|
53
|
-
# engine pre-binds these as ivar facts in the file-entry
|
|
54
|
-
# scope (slice 5b).
|
|
55
|
-
#
|
|
56
|
-
# ## Slice 5a scope
|
|
57
|
-
#
|
|
58
|
-
# **This file ships the value class + manifest hook ONLY.**
|
|
59
|
-
# The engine integration that (a) adds matched files to the
|
|
60
|
-
# analysis set, (b) narrows the file-entry `self_type`, and
|
|
61
|
-
# (c) pre-binds `bound_ivars` as ivar facts is **queued for
|
|
62
|
-
# slice 5b**, gated on demonstrated demand. The survey
|
|
63
|
-
# identifies only Redmine + tDiary as concrete consumers;
|
|
64
|
-
# premature engine work is deferred until those cases (or
|
|
65
|
-
# equivalents) materialise as committed plugin targets.
|
|
66
|
-
#
|
|
67
|
-
# With only this slice landed, plugin authors CAN declare a
|
|
68
|
-
# Tier D manifest entry today — the declaration round-trips
|
|
69
|
-
# through `Manifest#to_h` (cache-key stable) and is exposed
|
|
70
|
-
# on `Manifest#external_files` — but the substrate does not
|
|
71
|
-
# yet act on it. The contract is forward-compatible: when
|
|
72
|
-
# slice 5b lands, the engine reads the same declarations and
|
|
73
|
-
# plugin gems do not need to change.
|
|
74
|
-
class ExternalFile
|
|
75
|
-
attr_reader :glob, :receiver_type, :bound_ivars
|
|
76
|
-
|
|
77
|
-
def initialize(glob:, receiver_type:, bound_ivars: {})
|
|
78
|
-
validate_glob!(glob)
|
|
79
|
-
validate_receiver_type!(receiver_type)
|
|
80
|
-
validate_bound_ivars!(bound_ivars)
|
|
81
|
-
|
|
82
|
-
@glob = glob.dup.freeze
|
|
83
|
-
@receiver_type = receiver_type.dup.freeze
|
|
84
|
-
@bound_ivars = bound_ivars.to_h { |k, v| [k.dup.freeze, v.dup.freeze] }.freeze
|
|
85
|
-
freeze
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def to_h
|
|
89
|
-
{
|
|
90
|
-
"glob" => glob,
|
|
91
|
-
"receiver_type" => receiver_type,
|
|
92
|
-
"bound_ivars" => bound_ivars
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def ==(other)
|
|
97
|
-
other.is_a?(ExternalFile) && to_h == other.to_h
|
|
98
|
-
end
|
|
99
|
-
alias eql? ==
|
|
100
|
-
|
|
101
|
-
def hash
|
|
102
|
-
to_h.hash
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
def validate_glob!(value)
|
|
108
|
-
return if value.is_a?(String) && !value.empty?
|
|
109
|
-
|
|
110
|
-
raise ArgumentError,
|
|
111
|
-
"Plugin::Macro::ExternalFile#glob must be a non-empty String, got #{value.inspect}"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def validate_receiver_type!(value)
|
|
115
|
-
return if value.is_a?(String) && !value.empty?
|
|
116
|
-
|
|
117
|
-
raise ArgumentError,
|
|
118
|
-
"Plugin::Macro::ExternalFile#receiver_type must be a non-empty String, got #{value.inspect}"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def validate_bound_ivars!(value)
|
|
122
|
-
unless value.is_a?(Hash)
|
|
123
|
-
raise ArgumentError,
|
|
124
|
-
"Plugin::Macro::ExternalFile#bound_ivars must be a Hash, got #{value.inspect}"
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
value.each do |k, v|
|
|
128
|
-
unless k.is_a?(String) && k.start_with?("@") && k.length > 1
|
|
129
|
-
raise ArgumentError,
|
|
130
|
-
"Plugin::Macro::ExternalFile#bound_ivars key must be a String starting with `@`, " \
|
|
131
|
-
"got #{k.inspect}"
|
|
132
|
-
end
|
|
133
|
-
next if v.is_a?(String) && !v.empty?
|
|
134
|
-
|
|
135
|
-
raise ArgumentError,
|
|
136
|
-
"Plugin::Macro::ExternalFile#bound_ivars value must be a non-empty String, " \
|
|
137
|
-
"got #{v.inspect}"
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|