sonicop 26.8.110 → 26.8.111

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +15 -15
  3. data/Cargo.toml +4 -4
  4. data/README.ja.md +10 -7
  5. data/README.md +11 -8
  6. data/lib/sonicop/version.rb +1 -1
  7. data/src/cli.rs +123 -74
  8. data/src/engine.rs +166 -129
  9. data/src/rules/layout/alignment.rs +1 -1
  10. data/src/rules/layout/block_alignment.rs +1 -1
  11. data/src/rules/layout/closing_heredoc_indentation.rs +1 -1
  12. data/src/rules/layout/comment_indentation.rs +1 -1
  13. data/src/rules/layout/empty_line_after_guard_clause.rs +1 -1
  14. data/src/rules/layout/empty_lines.rs +1 -1
  15. data/src/rules/layout/empty_lines_around_access_modifier.rs +1 -1
  16. data/src/rules/layout/empty_lines_around_body.rs +1 -1
  17. data/src/rules/layout/empty_lines_around_method_body.rs +1 -6
  18. data/src/rules/layout/first_hash_element_indentation.rs +1 -1
  19. data/src/rules/layout/line_continuation_spacing.rs +1 -1
  20. data/src/rules/layout/line_length.rs +6 -3
  21. data/src/rules/layout/multiline_block_layout.rs +1 -1
  22. data/src/rules/lint/duplicate_magic_comment.rs +1 -1
  23. data/src/rules/lint/literal_in_interpolation.rs +4 -1
  24. data/src/rules/lint/ordered_magic_comments.rs +1 -1
  25. data/src/rules/lint/redundant_cop_disable_directive.rs +1 -1
  26. data/src/rules/style/comment_annotation.rs +5 -1
  27. data/src/rules/style/commented_keyword.rs +1 -1
  28. data/src/rules/style/document_dynamic_eval_definition.rs +1 -1
  29. data/src/rules/style/if_unless_modifier.rs +3 -3
  30. data/src/rules/style/redundant_format.rs +58 -52
  31. data/src/rules/style/redundant_regexp_argument.rs +1 -1
  32. data/src/rules/style/while_until_modifier.rs +1 -1
  33. data/src/rules/support.rs +23 -1
  34. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a00a7d0dc817fde927c8f9a5eb24db975f85aa2f4dda36ae1f7de548aa6557f6
4
- data.tar.gz: 2c09e86e186f680c8f31cf5fd0e979fb974bc23ad173fa14e4f100f98dc95ce3
3
+ metadata.gz: 8537845670d9f9608820455763080e3173cbc5ef1d15e9fee908b16196132eae
4
+ data.tar.gz: 63c63e6d899ff77c46d6608a179af0708d5bf69b03ced51311c1190b70d8922b
5
5
  SHA512:
6
- metadata.gz: 786c6eb994b2d5020f9f035623ec0f54ab95ad3f0359f747efd0bf97fd1bd4cca00d21b7a652b483861aef2af80dabb18dea4185bf9320a214307c4ea8780a8a
7
- data.tar.gz: 8c0b93e14e6e7a56f497cc55aad95ce730c681515f9e2f8d67efcfe3c8ae44d98dc8f5ab1bf9c25b60f6de0791b0ed077bc3df0b4a679d3c07416a731426c66d
6
+ metadata.gz: 58132ce002c9581b4d8d2b89b66e314eba80d123d530074b40f979b19df3fd92bc197d2183d3d21456ea2519789ab1db316098acb5d8db0e76446ef4a21583db
7
+ data.tar.gz: 12caa6a19a43a625a28209687d8d89866c724ff86850513b1dffdf18e3cd29f93683c97a6f673a2326c2329859946a818c01112ef5e62576b37a190c8df584b1
data/Cargo.lock CHANGED
@@ -4,9 +4,9 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "aho-corasick"
7
- version = "1.1.4"
7
+ version = "1.1.5"
8
8
  source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
9
+ checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
10
10
  dependencies = [
11
11
  "memchr",
12
12
  ]
@@ -135,9 +135,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
135
135
 
136
136
  [[package]]
137
137
  name = "clap"
138
- version = "4.6.4"
138
+ version = "4.6.5"
139
139
  source = "registry+https://github.com/rust-lang/crates.io-index"
140
- checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
140
+ checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf"
141
141
  dependencies = [
142
142
  "clap_builder",
143
143
  "clap_derive",
@@ -145,9 +145,9 @@ dependencies = [
145
145
 
146
146
  [[package]]
147
147
  name = "clap_builder"
148
- version = "4.6.2"
148
+ version = "4.6.5"
149
149
  source = "registry+https://github.com/rust-lang/crates.io-index"
150
- checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
150
+ checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078"
151
151
  dependencies = [
152
152
  "anstream",
153
153
  "anstyle",
@@ -276,9 +276,9 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
276
276
 
277
277
  [[package]]
278
278
  name = "find-msvc-tools"
279
- version = "0.1.10"
279
+ version = "0.1.11"
280
280
  source = "registry+https://github.com/rust-lang/crates.io-index"
281
- checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
281
+ checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
282
282
 
283
283
  [[package]]
284
284
  name = "foreign-types"
@@ -349,9 +349,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
349
349
 
350
350
  [[package]]
351
351
  name = "ignore"
352
- version = "0.4.31"
352
+ version = "0.4.32"
353
353
  source = "registry+https://github.com/rust-lang/crates.io-index"
354
- checksum = "7f8a7b8211e695a1d0cd91cace480d4d0bd57667ab10277cc412c5f7f4884f83"
354
+ checksum = "0b17771570a2b94107741a7b033f19132c2eee21d59d21b24d2ced26500bd66e"
355
355
  dependencies = [
356
356
  "crossbeam-deque",
357
357
  "globset",
@@ -587,9 +587,9 @@ dependencies = [
587
587
 
588
588
  [[package]]
589
589
  name = "regex-automata"
590
- version = "0.4.16"
590
+ version = "0.4.18"
591
591
  source = "registry+https://github.com/rust-lang/crates.io-index"
592
- checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
592
+ checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
593
593
  dependencies = [
594
594
  "aho-corasick",
595
595
  "memchr",
@@ -742,7 +742,7 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
742
742
 
743
743
  [[package]]
744
744
  name = "sonicop"
745
- version = "26.8.110"
745
+ version = "26.8.111"
746
746
  dependencies = [
747
747
  "anyhow",
748
748
  "assert_cmd",
@@ -847,8 +847,8 @@ checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782"
847
847
 
848
848
  [[package]]
849
849
  name = "tree-sitter-ruby"
850
- version = "26.8.101"
851
- source = "git+https://github.com/owayo/tree-sitter-ruby.git?rev=f1a19bb27ba64300d1032b813c51437a5a76ea33#f1a19bb27ba64300d1032b813c51437a5a76ea33"
850
+ version = "26.8.102"
851
+ source = "git+https://github.com/owayo/tree-sitter-ruby.git?tag=v26.8.102#b51a2c820a49b7270806a5480a003db4c183f8ff"
852
852
  dependencies = [
853
853
  "cc",
854
854
  "tree-sitter-language",
data/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sonicop"
3
- version = "26.8.110"
3
+ version = "26.8.111"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  description = "A fast, native RuboCop-compatible Ruby linter and formatter"
@@ -16,10 +16,10 @@ path = "src/main.rs"
16
16
 
17
17
  [dependencies]
18
18
  anyhow = "1.0.104"
19
- clap = { version = "4.6.4", features = ["derive", "wrap_help"] }
19
+ clap = { version = "4.6.5", features = ["derive", "wrap_help"] }
20
20
  encoding_rs = "0.8.35"
21
21
  globset = "0.4.19"
22
- ignore = "0.4.31"
22
+ ignore = "0.4.32"
23
23
  rayon = "1.12.0"
24
24
  regex = "1.13.1"
25
25
  serde = { version = "1.0.229", features = ["derive"] }
@@ -28,7 +28,7 @@ serde_yaml_ng = "0.10"
28
28
  shell-words = "1.1.1"
29
29
  tempfile = "3.27.0"
30
30
  tree-sitter = "0.26.11"
31
- tree-sitter-ruby = { git = "https://github.com/owayo/tree-sitter-ruby.git", rev = "f1a19bb27ba64300d1032b813c51437a5a76ea33" }
31
+ tree-sitter-ruby = { git = "https://github.com/owayo/tree-sitter-ruby.git", tag = "v26.8.102" }
32
32
  ureq = { version = "3.4", default-features = false, features = ["native-tls"] }
33
33
 
34
34
  [dev-dependencies]
data/README.ja.md CHANGED
@@ -165,13 +165,13 @@ Mastodon でバイト単位に一致します。この 2 つは死守ライン
165
165
 
166
166
  | コーパス | ファイル | RuboCop 並列 | Sonicop 並列 | RuboCop 単一 | Sonicop 単一 |
167
167
  |---|---:|---:|---:|---:|---:|
168
- | rubocop/rubocop | 1,765 | 11.49 秒 | **3.45 秒** | 42.49 秒 | **16.39 秒** |
169
- | mastodon/mastodon | 3,290 | 27.26 秒 | **6.03 秒** | 42.07 秒 | **19.53 秒** |
170
- | Homebrew/brew | 2,179 | 20.37 秒 | **5.36 秒** | 45.08 秒 | **16.03 秒** |
171
- | rails/rails | 3,551 | 45.89 秒 | **18.96 秒** | 99.56 秒 | **44.61 秒** |
172
- | ruby/ruby | 7,466 | 117.79 秒 | **54.97 秒** | 215.87 秒 | **100.62 秒** |
168
+ | rubocop/rubocop | 1,765 | 12.71 秒 | **4.59 秒** | 42.74 秒 | **13.75 秒** |
169
+ | mastodon/mastodon | 3,290 | 29.95 秒 | **6.48 秒** | 37.60 秒 | **15.88 秒** |
170
+ | Homebrew/brew | 2,179 | 18.20 秒 | **4.31 秒** | 38.72 秒 | **11.67 秒** |
171
+ | rails/rails | 3,551 | 52.55 秒 | **16.88 秒** | 162.55 秒 | **63.97 秒** |
172
+ | ruby/ruby | 7,466 | 132.17 秒 | **43.10 秒** | 199.78 秒 | **76.07 秒** |
173
173
 
174
- 差は並列で 2.1〜4.5 倍、単一プロセスで 2.12.8 倍と幅があり、1 コーパスでは代表できません。
174
+ 差は並列で 2.8〜4.6 倍、単一プロセスで 2.43.3 倍と幅があり、1 コーパスでは代表できません。
175
175
  **単一プロセスの列を読み、並列は目安として扱ってください。** 同じ 2 つのバイナリを 1 日に
176
176
  3 回測ったところ、単一プロセスの値は毎回 16% 以内に収まったのに対し、RuboCop 自身のツリーでの
177
177
  並列の倍率は、マシンが他に何をしていたかだけで 3.3 倍から 9.2 倍まで動きました。単一プロセスは
@@ -198,7 +198,10 @@ sonicop --force-default-config --format quiet
198
198
  ```
199
199
 
200
200
  測定機は Apple M2(8 コア)、Ruby 4.0.6(YJIT 利用可)、RubyGems 導入の RuboCop 1.89.0。
201
- 1 分平均のロードアベレージは、計測開始時が 3.6、終了時が 9.6 でした。**アイドル状態ではありません**。
201
+ 1 分平均のロードアベレージは、計測開始時が 4.0、終了時が 3.1 でした。**アイドル状態ではありません**。
202
+ RuboCop 自身のツリーだけは後から単独で測り直しています(負荷 3.7 → 4.0)。1 回目はリリース
203
+ ビルドの終わりがけに走ってしまい、値が 60% ほど高く出たためです。**負荷の違う 1 行を同じ表に
204
+ 並べることはできません**。
202
205
  両者を同じ条件で測っているため倍率は保たれますが、秒数そのものは下限ではなく、静かなマシンなら
203
206
  より速く出ます。コアを奪い合うものが動いていると両者とも膨らみ、その度合いは一致しません。それが
204
207
  並列の列があれだけ動く理由です。秒数そのものが重要なときは、他に負荷のない状態で測り、**実行の
data/README.md CHANGED
@@ -171,13 +171,13 @@ warmed runs.
171
171
 
172
172
  | Corpus | Files | RuboCop parallel | Sonicop parallel | RuboCop single | Sonicop single |
173
173
  |---|---:|---:|---:|---:|---:|
174
- | rubocop/rubocop | 1,765 | 11.49 s | **3.45 s** | 42.49 s | **16.39 s** |
175
- | mastodon/mastodon | 3,290 | 27.26 s | **6.03 s** | 42.07 s | **19.53 s** |
176
- | Homebrew/brew | 2,179 | 20.37 s | **5.36 s** | 45.08 s | **16.03 s** |
177
- | rails/rails | 3,551 | 45.89 s | **18.96 s** | 99.56 s | **44.61 s** |
178
- | ruby/ruby | 7,466 | 117.79 s | **54.97 s** | 215.87 s | **100.62 s** |
174
+ | rubocop/rubocop | 1,765 | 12.71 s | **4.59 s** | 42.74 s | **13.75 s** |
175
+ | mastodon/mastodon | 3,290 | 29.95 s | **6.48 s** | 37.60 s | **15.88 s** |
176
+ | Homebrew/brew | 2,179 | 18.20 s | **4.31 s** | 38.72 s | **11.67 s** |
177
+ | rails/rails | 3,551 | 52.55 s | **16.88 s** | 162.55 s | **63.97 s** |
178
+ | ruby/ruby | 7,466 | 132.17 s | **43.10 s** | 199.78 s | **76.07 s** |
179
179
 
180
- The gap is 2.1x to 4.5x in parallel and 2.1x to 2.8x single-process, so no single corpus summarizes
180
+ The gap is 2.8x to 4.6x in parallel and 2.4x to 3.3x single-process, so no single corpus summarizes
181
181
  it. **Read the single-process column and treat the parallel one as indicative.** Measuring the same
182
182
  two binaries three times over a day put the single-process figures within 16% of each other every
183
183
  time, while the parallel ratio on RuboCop's own tree moved between 3.3x and 9.2x purely with what
@@ -204,8 +204,11 @@ sonicop --force-default-config --format quiet
204
204
  ```
205
205
 
206
206
  Machine: Apple M2 (8 cores), Ruby 4.0.6 with YJIT available, RubyGems-installed RuboCop 1.89.0.
207
- The one-minute load average was 3.6 when the run started and 9.6 when it finished — the machine was
208
- in use, not idle. Both tools ran under the same conditions, so the ratios hold, but the absolute
207
+ The one-minute load average was 4.0 when the run started and 3.1 when it finished — the machine was
208
+ in use, not idle. RuboCop's own tree was re-measured on its own afterwards, at load 3.7 rising to 4.0,
209
+ because the first pass over it ran while a release build was still finishing and its numbers came out
210
+ 60% high; a row measured under a different load cannot sit in the same table as the others. Both tools
211
+ ran under the same conditions, so the ratios hold, but the absolute
209
212
  seconds are not a floor: expect better on a quiet machine. Anything competing for cores inflates
210
213
  both sides, and not by the same factor on each, which is what makes the parallel column move as much
211
214
  as it does. If the absolute numbers matter to you, measure on an idle machine and record the load
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Generated from Cargo.toml by `rake version:sync`. Do not edit by hand.
4
4
  module Sonicop
5
- VERSION = '26.8.110'
5
+ VERSION = '26.8.111'
6
6
  end
data/src/cli.rs CHANGED
@@ -390,6 +390,64 @@ fn try_run(cli: Cli, outputs: &[Option<PathBuf>]) -> Result<i32> {
390
390
  return Ok(0);
391
391
  }
392
392
 
393
+ let correct_mode = cli.correct_mode();
394
+ let selection = build_selection(&cli, &config, correct_mode)?;
395
+ if !cli.list_target_files {
396
+ warn_unimplemented_enabled(&config, &selection);
397
+ }
398
+
399
+ let parallel = !cli.no_parallel && (cli.parallel || cli.stdin.is_none());
400
+ let mut reports = match inspect_inputs(&cli, &cwd, &configs, &selection, parallel)? {
401
+ Inspection::Reports(reports) => reports,
402
+ Inspection::ListedTargets => return Ok(0),
403
+ };
404
+
405
+ if cli.debug {
406
+ debug_report(&cwd, &config, reports.len(), parallel);
407
+ }
408
+ if cli.auto_gen_config || cli.regenerate_todo {
409
+ generate_todo(&reports, &cwd, &cli)?;
410
+ return Ok(0);
411
+ }
412
+ if cli.disable_uncorrectable {
413
+ eprintln!(
414
+ "Sonicop: --disable-uncorrectable is accepted; todo insertion is not implemented yet."
415
+ );
416
+ }
417
+
418
+ let CorrectionRun {
419
+ reports: corrected_reports,
420
+ corrected_count,
421
+ stdin_corrected,
422
+ had_errors,
423
+ } = apply_corrections(reports, &cli, &configs, correct_mode, &selection)?;
424
+ reports = corrected_reports;
425
+
426
+ // A file RuboCop could not finish counts as a failed run even when nothing else offended.
427
+ let failing = fail_level.failing(&reports) || had_errors;
428
+
429
+ if let Some(corrected) = stdin_corrected {
430
+ print!("{corrected}");
431
+ } else {
432
+ filter_displayed_offenses(&mut reports, &cli, fail_level);
433
+ render_outputs(
434
+ &RenderRequest {
435
+ cli: &cli,
436
+ config: &config,
437
+ cwd: &cwd,
438
+ fail_level,
439
+ outputs,
440
+ corrected_count,
441
+ elapsed: started.elapsed().as_secs_f64(),
442
+ },
443
+ &reports,
444
+ )?;
445
+ }
446
+
447
+ Ok(i32::from(failing))
448
+ }
449
+
450
+ fn build_selection(cli: &Cli, config: &Config, correct_mode: CorrectMode) -> Result<Selection> {
393
451
  let mut only = csv(cli.only.as_deref());
394
452
  if cli.lint {
395
453
  only.push("Lint".to_owned());
@@ -397,10 +455,12 @@ fn try_run(cli: Cli, outputs: &[Option<PathBuf>]) -> Result<i32> {
397
455
  if cli.fix_layout {
398
456
  only.push("Layout".to_owned());
399
457
  }
400
- validate_selection(&only, "--only", &config)?;
458
+ validate_selection(&only, "--only", config)?;
459
+
401
460
  let except = csv(cli.except.as_deref());
402
- validate_selection(&except, "--except", &config)?;
403
- let selection = Selection {
461
+ validate_selection(&except, "--except", config)?;
462
+
463
+ Ok(Selection {
404
464
  only,
405
465
  except,
406
466
  disable_all: cli.disable_all_cops,
@@ -410,49 +470,40 @@ fn try_run(cli: Cli, outputs: &[Option<PathBuf>]) -> Result<i32> {
410
470
  safe_only: cli.safe,
411
471
  ignore_disable_comments: cli.ignore_disable_comments,
412
472
  display_suppressed: cli.display_suppressed,
413
- correcting: cli.correct_mode() != CorrectMode::None,
473
+ correcting: correct_mode != CorrectMode::None,
414
474
  // **No flag turns this on from the command line.** The guard is switched off for tests
415
475
  // only, and even then per case; a run reaches the environment variable or nothing.
416
476
  skip_syntax_guard: false,
417
- };
418
- let correct_mode = cli.correct_mode();
419
- if !cli.list_target_files {
420
- warn_unimplemented_enabled(&config, &selection);
421
- }
477
+ })
478
+ }
422
479
 
423
- let parallel = !cli.no_parallel && (cli.parallel || cli.stdin.is_none());
424
- let mut reports = if let Some(stdin_path) = &cli.stdin {
425
- if !cli.paths.is_empty() {
426
- bail!(
427
- "--stdin requires exactly one path supplied as its argument and no file arguments"
428
- );
429
- }
430
- let mut text = String::new();
431
- io::stdin()
432
- .read_to_string(&mut text)
433
- .context("failed to read UTF-8 source from stdin")?;
434
- let target_config = configs.for_path(stdin_path)?;
435
- vec![inspect_source(
436
- stdin_path.clone(),
437
- text,
438
- &target_config,
439
- &selection,
440
- )?]
441
- } else {
480
+ enum Inspection {
481
+ Reports(Vec<FileReport>),
482
+ ListedTargets,
483
+ }
484
+
485
+ fn inspect_inputs(
486
+ cli: &Cli,
487
+ cwd: &Path,
488
+ configs: &ConfigStore,
489
+ selection: &Selection,
490
+ parallel: bool,
491
+ ) -> Result<Inspection> {
492
+ let Some(stdin_path) = &cli.stdin else {
442
493
  let mut targets = discover_targets_with_store(
443
494
  &cli.paths,
444
- &cwd,
445
- &configs,
495
+ cwd,
496
+ configs,
446
497
  cli.force_exclusion,
447
498
  cli.only_recognized_file_types,
448
499
  )?;
449
500
  if cli.list_target_files {
450
501
  for path in targets {
451
- println!("{}", smart_path(&path, &cwd));
502
+ println!("{}", smart_path(&path, cwd));
452
503
  }
453
- return Ok(0);
504
+ return Ok(Inspection::ListedTargets);
454
505
  }
455
- if cli.fail_fast {
506
+ let reports = if cli.fail_fast {
456
507
  targets.sort_by_key(|path| {
457
508
  std::cmp::Reverse(
458
509
  fs::metadata(path)
@@ -460,45 +511,60 @@ fn try_run(cli: Cli, outputs: &[Option<PathBuf>]) -> Result<i32> {
460
511
  .ok(),
461
512
  )
462
513
  });
463
- inspect_fail_fast(&targets, &configs, &selection)?
514
+ inspect_fail_fast(&targets, configs, selection)?
464
515
  } else {
465
- inspect_files_with_store(&targets, &configs, &selection, parallel)?
466
- }
516
+ inspect_files_with_store(&targets, configs, selection, parallel)?
517
+ };
518
+ return Ok(Inspection::Reports(reports));
467
519
  };
468
520
 
469
- if cli.debug {
470
- debug_report(&cwd, &config, reports.len(), parallel);
471
- }
472
- if cli.auto_gen_config || cli.regenerate_todo {
473
- generate_todo(&reports, &cwd, &cli)?;
474
- return Ok(0);
475
- }
476
- if cli.disable_uncorrectable {
477
- eprintln!(
478
- "Sonicop: --disable-uncorrectable is accepted; todo insertion is not implemented yet."
479
- );
521
+ if !cli.paths.is_empty() {
522
+ bail!("--stdin requires exactly one path supplied as its argument and no file arguments");
480
523
  }
524
+ let mut text = String::new();
525
+ io::stdin()
526
+ .read_to_string(&mut text)
527
+ .context("failed to read UTF-8 source from stdin")?;
528
+ let target_config = configs.for_path(stdin_path)?;
529
+ let report = inspect_source(stdin_path.clone(), text, &target_config, selection)?;
530
+ Ok(Inspection::Reports(vec![report]))
531
+ }
532
+
533
+ struct CorrectionRun {
534
+ reports: Vec<FileReport>,
535
+ corrected_count: usize,
536
+ stdin_corrected: Option<String>,
537
+ had_errors: bool,
538
+ }
481
539
 
540
+ fn apply_corrections(
541
+ reports: Vec<FileReport>,
542
+ cli: &Cli,
543
+ configs: &ConfigStore,
544
+ correct_mode: CorrectMode,
545
+ selection: &Selection,
546
+ ) -> Result<CorrectionRun> {
482
547
  let mut corrected_count = 0;
483
548
  let mut stdin_corrected = None;
484
- let mut run_errors = 0;
549
+ let mut had_errors = false;
485
550
  let mut corrected_reports = Vec::with_capacity(reports.len());
551
+
486
552
  for report in reports {
487
553
  let path = report.path.clone();
488
554
  let target_config = configs.for_path(&path)?;
489
- let outcome = correct_file(report, correct_mode, &target_config, &selection)?;
555
+ let outcome = correct_file(report, correct_mode, &target_config, selection)?;
490
556
  corrected_count += outcome.corrected_count;
491
557
  if let Some(message) = outcome.infinite_loop {
492
558
  // RuboCop keeps the run going and still writes what it managed to correct.
493
559
  eprintln!("{message}");
494
- run_errors += 1;
560
+ had_errors = true;
495
561
  }
496
562
  // Not an offense: the file on disk parses, so a reader told to look for a syntax error
497
563
  // would find none. It is an autocorrect failure, and it has to reach the exit code --
498
564
  // a `-A` run that silently declined to correct must not look like a clean one to CI.
499
565
  if let Some(message) = outcome.rollback {
500
566
  eprintln!("{message}");
501
- run_errors += 1;
567
+ had_errors = true;
502
568
  }
503
569
  if outcome.rewritten {
504
570
  if cli.stdin.is_some() {
@@ -509,30 +575,13 @@ fn try_run(cli: Cli, outputs: &[Option<PathBuf>]) -> Result<i32> {
509
575
  }
510
576
  corrected_reports.push(outcome.report);
511
577
  }
512
- reports = corrected_reports;
513
-
514
- // A file RuboCop could not finish counts as a failed run even when nothing else offended.
515
- let failing = fail_level.failing(&reports) || run_errors > 0;
516
-
517
- if let Some(corrected) = stdin_corrected {
518
- print!("{corrected}");
519
- } else {
520
- filter_displayed_offenses(&mut reports, &cli, fail_level);
521
- render_outputs(
522
- &RenderRequest {
523
- cli: &cli,
524
- config: &config,
525
- cwd: &cwd,
526
- fail_level,
527
- outputs,
528
- corrected_count,
529
- elapsed: started.elapsed().as_secs_f64(),
530
- },
531
- &reports,
532
- )?;
533
- }
534
578
 
535
- Ok(i32::from(failing))
579
+ Ok(CorrectionRun {
580
+ reports: corrected_reports,
581
+ corrected_count,
582
+ stdin_corrected,
583
+ had_errors,
584
+ })
536
585
  }
537
586
 
538
587
  /// RuboCop's `--fail-level` accepts a severity plus the pseudo level `autocorrect`, which changes