audition 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +125 -49
- data/lib/audition/bundle_sweep.rb +25 -15
- data/lib/audition/cli.rb +128 -140
- data/lib/audition/config.rb +30 -4
- data/lib/audition/dynamic/harness.rb +415 -41
- data/lib/audition/dynamic/prober.rb +271 -54
- data/lib/audition/finding.rb +14 -2
- data/lib/audition/progress.rb +418 -0
- data/lib/audition/reconciliation.rb +86 -0
- data/lib/audition/report/github.rb +4 -3
- data/lib/audition/report/json.rb +5 -1
- data/lib/audition/report/sweep.rb +182 -0
- data/lib/audition/report/text.rb +13 -2
- data/lib/audition/report.rb +8 -1
- data/lib/audition/rewriters.rb +29 -2
- data/lib/audition/static/analyzer.rb +86 -17
- data/lib/audition/static/checks/dependency_class_state.rb +160 -0
- data/lib/audition/static/checks/instance_memoization.rb +226 -0
- data/lib/audition/static/checks/mutable_constants.rb +172 -9
- data/lib/audition/static/checks/ractor_isolation.rb +214 -15
- data/lib/audition/static/checks/unsafe_calls.rb +7 -4
- data/lib/audition/static/checks/unshareable_reads.rb +259 -0
- data/lib/audition/static/checks.rb +6 -2
- data/lib/audition/static/gem_calls.rb +1770 -0
- data/lib/audition/static/graph_audit.rb +1113 -15
- data/lib/audition/static/literal_classifier.rb +385 -20
- data/lib/audition/static/native_extensions.rb +28 -16
- data/lib/audition/static/work_split.rb +54 -0
- data/lib/audition/target.rb +82 -13
- data/lib/audition/version.rb +1 -1
- data/lib/audition.rb +3 -0
- metadata +12 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c47dfccb03a9d164583e3ae0a5530c74a41cc176f62865807fa4a4d1ed11d7f4
|
|
4
|
+
data.tar.gz: 75697dacb090c469d8f2966d5174fb08186e1808b85737872b0db5f6034a688d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a631d1f7cd378ff04c47dcffb9608d332bfb048fc37dbda66179b085c487e936206359b3ea61a1e8fb5c70d121b6b7d94275b2e6d436f32772e1ea68a540fbd4
|
|
7
|
+
data.tar.gz: 974bbcd0a531588edf1d2474e9c8afb7bd5d3869a985b483ebb2c628007837ed20e50d10a580f6f0204db95e42128bb3de061237bbdaeab28f3d7008d7838a37
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Point it at a Ruby script, a gem, a Rack app, or a Rails root and it
|
|
4
4
|
tells you whether that code can run inside Ractors, why it cannot,
|
|
5
|
-
and how to fix it. Unlike a linter,
|
|
5
|
+
and how to fix it. Unlike a linter, Audition does not stop at
|
|
6
6
|
pattern-matching your source: whole-program analysis is powered by
|
|
7
7
|
[rubydex](https://github.com/Shopify/rubydex), Shopify's Ruby
|
|
8
8
|
indexer, and the target is also loaded in a sandboxed subprocess
|
|
@@ -28,12 +28,13 @@ core itself is being ractorized; see the
|
|
|
28
28
|
(which rule of the Ractor model it violates) and a `fix`
|
|
29
29
|
(what to write instead).
|
|
30
30
|
- **`--fix` like RuboCop, in two tiers.** Safe corrections:
|
|
31
|
-
`.freeze` on string constants
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
`.freeze` on string constants (literals as well as the fresh
|
|
32
|
+
strings core methods return), sentinels, and containers whose
|
|
33
|
+
elements are all shareable, and boot-time hoisting of
|
|
34
|
+
method-body requires. `--fix-unsafe` adds semantics-affecting
|
|
35
|
+
rewrites: `Ractor.make_shareable(...)` for the remaining mutable
|
|
36
|
+
and shallow-frozen containers, for containers a core method
|
|
37
|
+
allocates, and for Proc constants, magic-comment insertion,
|
|
37
38
|
freeze-on-memoize for class-level memoization (both `@x ||=`
|
|
38
39
|
and `return @x if defined?(@x)` idioms keep their caching, the
|
|
39
40
|
memoized value becomes shareable, Rails-core style;
|
|
@@ -47,7 +48,7 @@ core itself is being ractorized; see the
|
|
|
47
48
|
state, so `globalid` is not blamed for ActiveSupport's state.
|
|
48
49
|
- **Dogfooding.** The scanner for Ractor compatibility is built
|
|
49
50
|
using Ractors: static analysis fans out across CPU cores on
|
|
50
|
-
Ractor workers, and
|
|
51
|
+
Ractor workers, and Audition passes its own audit. It runs on
|
|
51
52
|
itself on every commit (a lefthook pre-commit over the staged
|
|
52
53
|
files) and on every push (a full self-audit in CI).
|
|
53
54
|
- **Trained on Rails core.** Several checks and fix suggestions
|
|
@@ -58,26 +59,24 @@ core itself is being ractorized; see the
|
|
|
58
59
|
findings are documented in
|
|
59
60
|
[docs/rails_core_best_practices.md](docs/rails_core_best_practices.md).
|
|
60
61
|
- **Terminal-native output.** Colors, glyphs, and OSC 8 hyperlinks;
|
|
61
|
-
`path:line` is clickable in supporting terminals.
|
|
62
|
-
CI.
|
|
62
|
+
`path:line` is clickable in supporting terminals. Long scans
|
|
63
|
+
narrate their phases on stderr. JSON output for CI.
|
|
63
64
|
|
|
64
65
|
```console
|
|
65
66
|
$ audition worker.rb
|
|
66
|
-
*
|
|
67
|
+
* Audition 0.3.0 ruby 4.0.6 · script at .
|
|
67
68
|
|
|
68
69
|
worker.rb
|
|
69
|
-
x
|
|
70
|
-
access global
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
per-Ractor state use Ractor.current[:key].
|
|
80
|
-
x worker.rb:4 read of global variable $jobs
|
|
70
|
+
x worker.rb:1 write to global variable $jobs global-variables
|
|
71
|
+
why: Non-main Ractors cannot access global variables; this raises
|
|
72
|
+
Ractor::IsolationError the moment the line executes in a Ractor
|
|
73
|
+
(verified on Ruby 4.0).
|
|
74
|
+
fix: Pass the value into the Ractor explicitly (Ractor.new(value) {
|
|
75
|
+
|v| ... }) or over a Ractor::Port; for per-Ractor state use
|
|
76
|
+
Ractor.current[:key]; do one-time process setup on the main Ractor
|
|
77
|
+
before spawning.
|
|
78
|
+
x worker.rb:1 raises inside a Ractor: Ractor::IsolationError ...
|
|
79
|
+
x worker.rb:4 read of global variable $jobs global-variables
|
|
81
80
|
...
|
|
82
81
|
|
|
83
82
|
dynamic probes
|
|
@@ -93,16 +92,29 @@ And the whole-bundle view:
|
|
|
93
92
|
|
|
94
93
|
```console
|
|
95
94
|
$ audition Gemfile.lock --static-only
|
|
96
|
-
|
|
97
|
-
│
|
|
98
|
-
|
|
99
|
-
│
|
|
100
|
-
|
|
101
|
-
│
|
|
102
|
-
│
|
|
103
|
-
│
|
|
104
|
-
|
|
105
|
-
0
|
|
95
|
+
╭───────────────────────────────────────────────────────────────────────────────────────╮
|
|
96
|
+
│ Audition bundle sweep │
|
|
97
|
+
├──────────┬──────────┬─────────────┬────────┬────────────┬──────────┬─────────┬────────┤
|
|
98
|
+
│ gem │ version │ verdict │ errors │ dep errors │ warnings │ fixable │ status │
|
|
99
|
+
├──────────┼──────────┼─────────────┼────────┼────────────┼──────────┼─────────┼────────┤
|
|
100
|
+
│ rubocop │ 1.88.2 │ x not ready │ 194 │ - │ 203 │ 28 │ ok │
|
|
101
|
+
│ parser │ 3.3.12.0 │ x not ready │ 154 │ - │ 26 │ 26 │ ok │
|
|
102
|
+
│ rack │ 3.2.7 │ x not ready │ 31 │ - │ 61 │ 13 │ ok │
|
|
103
|
+
│ rake │ 13.4.2 │ x not ready │ 30 │ - │ 24 │ 5 │ ok │
|
|
104
|
+
│ prism │ 1.9.0 │ x not ready │ 16 │ - │ 88 │ 15 │ ok │
|
|
105
|
+
│ json │ 2.21.2 │ x not ready │ 7 │ - │ 5 │ - │ ok │
|
|
106
|
+
│ pastel │ 0.8.0 │ x not ready │ 2 │ - │ 1 │ - │ ok │
|
|
107
|
+
│ tty-link │ 0.2.0 │ x not ready │ 1 │ - │ 1 │ - │ ok │
|
|
108
|
+
╰──────────┴──────────┴─────────────┴────────┴────────────┴──────────┴─────────┴────────╯
|
|
109
|
+
|
|
110
|
+
x 0 of 8 gems ractor-ready · 8 not ready
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Verdicts are colored by severity, clean counts are left blank, and
|
|
114
|
+
the sweep names each gem as it finishes:
|
|
115
|
+
|
|
116
|
+
```console
|
|
117
|
+
◆ Audition sweep rubocop 8/8 (100%) 5.8s
|
|
106
118
|
```
|
|
107
119
|
|
|
108
120
|
**Requires Ruby 4.0 or newer**, strictly: the tool targets the modern
|
|
@@ -172,6 +184,20 @@ Useful flags:
|
|
|
172
184
|
| `--capabilities` | table of what this Ruby allows in Ractors |
|
|
173
185
|
| `--timeout 60` | dynamic probe budget in seconds |
|
|
174
186
|
| `--plain` | no colors or hyperlinks (also via NO_COLOR, pipes) |
|
|
187
|
+
| `--progress` / `--no-progress` | force / suppress the stderr narration |
|
|
188
|
+
| `-j 4` / `--workers 4` | scan Ractors (default: cores, capped by `RUBY_MAX_CPU`) |
|
|
189
|
+
|
|
190
|
+
Long runs narrate themselves on stderr, so stdout stays pipeable:
|
|
191
|
+
a rewriting status line on a terminal, one line per phase off it.
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
◆ Audition checking 50/919 5% (0.2s, on 8 ractors)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
A phase running in Ractors says how many. The narration turns on
|
|
198
|
+
by itself for a large tree or a bundle sweep and stays off for
|
|
199
|
+
`--format json` and `--format github`; the flags force either
|
|
200
|
+
way.
|
|
175
201
|
|
|
176
202
|
Exit codes: `0` clean, `1` findings at or above the `--fail-on`
|
|
177
203
|
threshold (or a failed dynamic probe), `2` usage error.
|
|
@@ -210,11 +236,20 @@ timeout: 60
|
|
|
210
236
|
exclude:
|
|
211
237
|
- legacy/**
|
|
212
238
|
- db/schema.rb
|
|
239
|
+
test_dirs:
|
|
240
|
+
- qa
|
|
213
241
|
checks:
|
|
214
242
|
disable:
|
|
215
243
|
- at-exit
|
|
216
244
|
```
|
|
217
245
|
|
|
246
|
+
`test_dirs` names the directories that hold tests rather than
|
|
247
|
+
code a production boot loads; findings in them are tagged
|
|
248
|
+
`test` and rated as test code. It defaults to `test`, `spec`,
|
|
249
|
+
`features`, and replaces that list rather than adding to it—the
|
|
250
|
+
`_test.rb` and `_spec.rb` suffixes always count, whatever it
|
|
251
|
+
says.
|
|
252
|
+
|
|
218
253
|
## CI and git hooks
|
|
219
254
|
|
|
220
255
|
**GitHub Actions.** `--format github` turns findings into
|
|
@@ -260,7 +295,7 @@ With [pre-commit](https://pre-commit.com):
|
|
|
260
295
|
- repo: local
|
|
261
296
|
hooks:
|
|
262
297
|
- id: audition
|
|
263
|
-
name:
|
|
298
|
+
name: Audition
|
|
264
299
|
language: system
|
|
265
300
|
entry: audition --static-only --plain
|
|
266
301
|
types: [ruby]
|
|
@@ -289,14 +324,29 @@ Static, with file:line precision:
|
|
|
289
324
|
- **Class variables**, resolved on the rubydex graph.
|
|
290
325
|
- **Class-level instance variables**, unified across the class
|
|
291
326
|
body, `def self.`, and `class << self`, across files; the classic
|
|
292
|
-
`@cache ||= {}` and `return @x if defined?(@x)` memoizations.
|
|
327
|
+
`@cache ||= {}` and `return @x if defined?(@x)` memoizations. A
|
|
328
|
+
memo whose write is proxied to the main Ractor
|
|
329
|
+
(`@x || on_main(self) { @x ||= ... }`) is reported as that
|
|
330
|
+
escape hatch, not as a raw memo.
|
|
293
331
|
- **Constants that are not deeply shareable**: bare mutable
|
|
294
332
|
literals, interpolated strings, the subtle shallow freeze
|
|
295
|
-
(`[[1], [2]].freeze` still raises;
|
|
296
|
-
call results the magic comment never covers
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
333
|
+
(`[[1], [2]].freeze` still raises; Audition explains why), and
|
|
334
|
+
call results the magic comment never covers, from the
|
|
335
|
+
return-type contracts of core methods: fresh strings
|
|
336
|
+
(`X.tr(":", "")`, `[8, 2, 0].join(".")`, `Regexp.new`,
|
|
337
|
+
`format`), fresh containers (`TYPES.keys`, `LIST.map { }`,
|
|
338
|
+
`BASE + [:x]`, `DEFAULTS.merge(...)`, `.dup`), string splitters
|
|
339
|
+
under a shallow freeze (`".*".chars.freeze`), and Method
|
|
340
|
+
objects, which no freeze makes shareable. A spec executes the
|
|
341
|
+
tables against the running Ruby. Integer arithmetic,
|
|
342
|
+
comparisons, and negation are recognized as shareable. Honors
|
|
343
|
+
`# frozen_string_literal:` and `# shareable_constant_value:`
|
|
344
|
+
magic comments.
|
|
345
|
+
- **Instance memoization on classes that get frozen**: a lazy
|
|
346
|
+
`@x ||=` on a class whose initialize freezes self raises
|
|
347
|
+
FrozenError on first use; on a class with a `freeze` override
|
|
348
|
+
the memo must be warmed inside the override before `super`
|
|
349
|
+
(compute on freeze), and one left cold is reported.
|
|
300
350
|
- **Sync primitives and Procs in constants** (Mutex, Queue,
|
|
301
351
|
lambdas), including `Hash.new { }` default procs, which stay
|
|
302
352
|
unshareable even after `.freeze`.
|
|
@@ -307,7 +357,12 @@ Static, with file:line precision:
|
|
|
307
357
|
- **Runtime require and autoload** (serializes all Ractors through
|
|
308
358
|
the main-Ractor proxy).
|
|
309
359
|
- **`Ractor.new` blocks capturing outer locals** (the ArgumentError
|
|
310
|
-
at creation time), resolved through Prism's exact scope depths
|
|
360
|
+
at creation time), resolved through Prism's exact scope depths;
|
|
361
|
+
and **blocks that `Ractor.shareable_proc` would refuse**, handed
|
|
362
|
+
to it directly or to a Rails callback macro (`before_create`,
|
|
363
|
+
`validate`, `on_load`, ...), because a captured local holds a
|
|
364
|
+
provably unshareable value (`prefix = +"Draft: "`) or is
|
|
365
|
+
assigned more than once.
|
|
311
366
|
- **Hostile or removed APIs**: `Ractor.yield`/`take` (gone in 4.0),
|
|
312
367
|
ActiveSupport `cattr_*`/`mattr_*` class variables (with the
|
|
313
368
|
`class_attribute` migration Rails itself made) and
|
|
@@ -317,20 +372,35 @@ Static, with file:line precision:
|
|
|
317
372
|
scan of every compiled `.bundle`/`.so` for the
|
|
318
373
|
`rb_ext_ractor_safe` import, which also covers precompiled
|
|
319
374
|
platform gems that ship no sources; an unbuilt checkout is
|
|
320
|
-
scanned at the source level (
|
|
375
|
+
scanned at the source level instead (C, Rust, or Zig sources
|
|
376
|
+
beside their `extconf.rb`, `Cargo.toml` or `build.zig`).
|
|
321
377
|
A silent extension raises `Ractor::UnsafeError` on every call
|
|
322
378
|
from a non-main Ractor, so it rates a warning; a declared one
|
|
323
379
|
gets an info note, because the declaration is the maintainer's
|
|
324
380
|
assertion, not a proof.
|
|
381
|
+
- **The static pass's own blind spots**: where rubydex reports an
|
|
382
|
+
expression it could not resolve and the shape could hide what
|
|
383
|
+
the checks above look for, the hole is reported rather than
|
|
384
|
+
read as a clean line — a singleton opened on a runtime receiver
|
|
385
|
+
that writes class-level state, a superclass or `include`
|
|
386
|
+
argument computed at runtime, a constant assigned through an
|
|
387
|
+
unresolved path. A clean report for such a class covers only
|
|
388
|
+
what the class itself declares; the dynamic probe reaches the
|
|
389
|
+
rest.
|
|
325
390
|
|
|
326
391
|
Dynamic, on the live object graph:
|
|
327
392
|
|
|
328
393
|
- Runs scripts inside a real Ractor (via `load`, which is not
|
|
329
394
|
proxied) and reports the actual exception.
|
|
330
|
-
- Requires a library, then sweeps every constant it introduced
|
|
395
|
+
- Requires a library, then sweeps every constant it introduced,
|
|
396
|
+
under new namespaces and pre-existing ones alike, with
|
|
331
397
|
`Ractor.shareable?`, and inspects every class and module for
|
|
332
398
|
class-level ivars and class variables, with
|
|
333
|
-
`const_source_location` attribution.
|
|
399
|
+
`const_source_location` attribution. What the sweep proves
|
|
400
|
+
shareable retires the static guesses about the same objects:
|
|
401
|
+
an unproven constant warning disappears, and class-level state
|
|
402
|
+
that held only shareable values after boot keeps an info note
|
|
403
|
+
instead of an error.
|
|
334
404
|
- Records every compiled extension the load pulled in,
|
|
335
405
|
dependencies included, and byte-scans each for the
|
|
336
406
|
`rb_ext_ractor_safe` import; silent ones are reported against
|
|
@@ -340,13 +410,19 @@ Dynamic, on the live object graph:
|
|
|
340
410
|
the per-worker model of Ractor web servers; then hammers it from
|
|
341
411
|
4 Ractors x 25 requests to surface failures that only appear
|
|
342
412
|
under concurrency.
|
|
343
|
-
- Boots Rails (`config/environment.rb`)
|
|
344
|
-
|
|
413
|
+
- Boots Rails (`config/environment.rb`) with
|
|
414
|
+
`unshareable_proc_action` armed, so every callback block Rails
|
|
415
|
+
cannot make shareable is reported at the Proc's definition site;
|
|
416
|
+
eager-loads; on Rails 8.2 calls `ractorize!` to freeze the
|
|
417
|
+
application graph, then serves one GET / on the main Ractor
|
|
418
|
+
(where a lazy memo on a now-frozen object raises FrozenError)
|
|
419
|
+
and one inside a Ractor; and sweeps the application's
|
|
420
|
+
namespaces. Without `ractorize!` an info note says so.
|
|
345
421
|
|
|
346
422
|
## Agent skill
|
|
347
423
|
|
|
348
424
|
This repository ships a `ractor-readiness` skill that teaches
|
|
349
|
-
coding agents (Claude Code and friends) the full
|
|
425
|
+
coding agents (Claude Code and friends) the full Audition
|
|
350
426
|
workflow: audit, fix tiers, suite-parity verification, and
|
|
351
427
|
incremental adoption. It lives in
|
|
352
428
|
[skills/ractor-readiness/SKILL.md](skills/ractor-readiness/SKILL.md).
|
|
@@ -402,7 +478,7 @@ bundle exec exe/audition --capabilities
|
|
|
402
478
|
```
|
|
403
479
|
|
|
404
480
|
Static scanning is Ractor-parallel on large targets (one worker
|
|
405
|
-
per core, minus one for the main Ractor);
|
|
481
|
+
per core, minus one for the main Ractor); Audition's own `lib/`
|
|
406
482
|
passes `audition lib` clean.
|
|
407
483
|
|
|
408
484
|
The design notes in `docs/design.md` include the empirically
|
|
@@ -413,7 +489,7 @@ calibrated against.
|
|
|
413
489
|
|
|
414
490
|
The whole-program checks stand on
|
|
415
491
|
[rubydex](https://github.com/Shopify/rubydex), Shopify's
|
|
416
|
-
high-performance static analysis suite for Ruby:
|
|
492
|
+
high-performance static analysis suite for Ruby: Audition feeds
|
|
417
493
|
every file into its graph and reads state ownership back out.
|
|
418
494
|
Thanks to its authors, in particular the top five contributors:
|
|
419
495
|
[Alexandre Terrasa](https://github.com/Morriar),
|
|
@@ -31,10 +31,10 @@ module Audition
|
|
|
31
31
|
|
|
32
32
|
# Audits every locked gem and ranks the results, worst first.
|
|
33
33
|
#
|
|
34
|
-
# @param progress [
|
|
35
|
-
#
|
|
34
|
+
# @param progress [Progress] named per gem as each finishes,
|
|
35
|
+
# which is the only unit a sweep has
|
|
36
36
|
# @return [Array<Row>]
|
|
37
|
-
def rows(progress:
|
|
37
|
+
def rows(progress: Progress::SILENT)
|
|
38
38
|
gems = locked_gems
|
|
39
39
|
queue = Thread::Queue.new
|
|
40
40
|
gems.each { |g| queue << g }
|
|
@@ -42,17 +42,19 @@ module Audition
|
|
|
42
42
|
|
|
43
43
|
collected = []
|
|
44
44
|
mutex = Thread::Mutex.new
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
progress.phase("sweep", total: gems.size, unit: "gems") do
|
|
46
|
+
@concurrency.times.map do
|
|
47
|
+
Thread.new do
|
|
48
|
+
while (name, version = queue.pop)
|
|
49
|
+
row = audit_gem(name, version)
|
|
50
|
+
mutex.synchronize do
|
|
51
|
+
collected << row
|
|
52
|
+
progress.item(row.name)
|
|
53
|
+
end
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
+
end.each(&:join)
|
|
57
|
+
end
|
|
56
58
|
|
|
57
59
|
collected.sort_by do |row|
|
|
58
60
|
[VERDICT_ORDER.fetch(row.verdict), -row.errors, row.name]
|
|
@@ -78,6 +80,7 @@ module Audition
|
|
|
78
80
|
findings = filter(static_findings(target, config),
|
|
79
81
|
directives, config)
|
|
80
82
|
results = dynamic_results(target)
|
|
83
|
+
findings = Reconciliation.apply(findings, results)
|
|
81
84
|
findings += filter(results.flat_map(&:findings),
|
|
82
85
|
directives, config)
|
|
83
86
|
report = Report.new(
|
|
@@ -115,8 +118,10 @@ module Audition
|
|
|
115
118
|
ruby_files: spec.require_paths.flat_map do |rp|
|
|
116
119
|
ruby_files_under(File.join(root, rp))
|
|
117
120
|
end,
|
|
118
|
-
entry: {mode: :require, feature: spec.name,
|
|
119
|
-
|
|
121
|
+
entry: {mode: :require, feature: spec.name,
|
|
122
|
+
load_paths: spec.full_require_paths, root: root},
|
|
123
|
+
compiled_files: Target.compiled_for(spec),
|
|
124
|
+
stub_files: Target.stubs(root)
|
|
120
125
|
)
|
|
121
126
|
end
|
|
122
127
|
|
|
@@ -148,7 +153,12 @@ module Audition
|
|
|
148
153
|
end
|
|
149
154
|
per_file = Static::Analyzer.new
|
|
150
155
|
.analyze_paths(files, workers: 1)
|
|
151
|
-
|
|
156
|
+
gem_calls = Static::GemCalls
|
|
157
|
+
.new(root: target.root, stubs: target.stub_files)
|
|
158
|
+
.analyze_paths(files)
|
|
159
|
+
per_file + gem_calls + Static::GraphAudit.new
|
|
160
|
+
.analyze_paths(files, constant_findings: per_file + gem_calls,
|
|
161
|
+
workers: 1) +
|
|
152
162
|
Static::NativeExtensions.new.analyze(target,
|
|
153
163
|
compiled_files: compiled)
|
|
154
164
|
end
|