cargo-gamma 0.4.0

Fast mutation testing for Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![cfg(not(all(test, miri)))]

//! Fast mutation testing for Rust.
//!
//! * [Why mutation testing?](#why-mutation-testing)
//!   * [Mutation testing vs. coverage and fuzzing](#mutation-testing-vs-coverage-and-fuzzing)
//! * [Why `cargo-gamma`?](#why-cargo-gamma)
//! * [Getting started](#getting-started)
//! * [Mutators](#mutators)
//! * [Safety and test side effects](#safety-and-test-side-effects)
//! * [Run phases](#run-phases)
//!   * [Conditional compilation](#conditional-compilation)
//!   * [Doctests](#doctests)
//!   * [Scoping a run](#scoping-a-run)
//!   * [Differential mutation testing](#differential-mutation-testing)
//!     * [Compile-fail targets](#compile-fail-targets)
//!   * [Controlling a run](#controlling-a-run)
//! * [Verdicts and the score](#verdicts-and-the-score)
//!   * [How the mutation score is calculated](#how-the-mutation-score-is-calculated)
//!   * [Why 100% line coverage can still have uncovered mutants](#why-100-line-coverage-can-still-have-uncovered-mutants)
//!   * [Why some mutants cannot be killed](#why-some-mutants-cannot-be-killed)
//!   * [Acting on surviving mutants](#acting-on-surviving-mutants)
//! * [Killing mutants](#killing-mutants)
//!   * [Fixing timeouts, out-of-memory and unviable mutants](#fixing-timeouts-out-of-memory-and-unviable-mutants)
//!     * [Taking suppressions back out](#taking-suppressions-back-out)
//!   * [Hangs](#hangs)
//!     * [Bounding what a mutant allocates](#bounding-what-a-mutant-allocates)
//!     * [Baseline and intermittent test failures](#baseline-and-intermittent-test-failures)
//! * [Suppressing mutations](#suppressing-mutations)
//!   * [Choosing a channel](#choosing-a-channel)
//!   * [In-source directives](#in-source-directives)
//!     * [Selecting what to suppress](#selecting-what-to-suppress)
//!     * [Directive arguments](#directive-arguments)
//!     * [When a suppression stops earning its place](#when-a-suppression-stops-earning-its-place)
//!     * [The comment form](#the-comment-form)
//!   * [Where a directive can go](#where-a-directive-can-go)
//!   * [Stating what a site's fate should be](#stating-what-a-sites-fate-should-be)
//! * [Configuration](#configuration)
//!   * [Where the file lives](#where-the-file-lives)
//!   * [Precedence](#precedence)
//! * [Known gaps](#known-gaps)
//! * [Reports](#reports)
//! * [Improving mutation performance](#improving-mutation-performance)
//!   * [Where the time goes](#where-the-time-goes)
//!   * [Optimizing compute-heavy suites](#optimizing-compute-heavy-suites)
//!   * [Checking in the hints file](#checking-in-the-hints-file)
//!   * [Running only the tests that reach the mutant](#running-only-the-tests-that-reach-the-mutant)
//!   * [Incremental runs](#incremental-runs)
//!   * [What a run remembers](#what-a-run-remembers)
//!   * [Diagnosing a slow run](#diagnosing-a-slow-run)
//!     * [Reporting a run that went wrong](#reporting-a-run-that-went-wrong)
//!   * [What to reach for](#what-to-reach-for)
//! * [Continuous integration](#continuous-integration)
//!   * [Exit codes](#exit-codes)
//!   * [Example GitHub Actions workflow](#example-github-actions-workflow)
//!   * [Sharding](#sharding)
//!
//! A few additional docs of interest:
//!
//! | Document | What it holds |
//! | --- | --- |
//! | [docs/CMDLINE.md](docs/CMDLINE.md) | Every subcommand and option, grouped by category. |
//! | [docs/CONFIG.md](docs/CONFIG.md) | Every configuration key, with examples. |
//! | [docs/MIGRATION.md](docs/MIGRATION.md) | Breaking changes and upgrade guidance. |
//! | [docs/MUTATORS.md](docs/MUTATORS.md) | Every mutator and profile, and what each one asks of a suite. |
//! | [docs/DESIGN.md](docs/DESIGN.md) | How the tool works internally. |
//! | [docs/gamma.toml](docs/gamma.toml) | A fully documented configuration file to copy as a starting point. |
//!
//! ## Why mutation testing?
//!
//! **Tests check your code. `cargo-gamma` checks your tests.**
//!
//! Line coverage tells you which lines ran, but it cannot tell you whether your tests would notice if a
//! line were wrong. Mutation testing answers that question directly: it introduces small, deliberate bugs
//! ("mutants") into your code and runs your test suite to see if your tests notice:
//!
//! * **Killed mutant:** A test failed! Your test suite caught the bug.
//! * **Surviving mutant:** All tests passed. Your test suite didn't notice the change, pointing out a
//!   blind spot or missing assertion with an exact file, line number, and diff.
//!
//! As AI-generated tests proliferate, mutation testing is an important deterministic safety measure to
//! ensure that a generated test suite actually, you know, tests stuff. Without mutation testing, AI can easily
//! gas light you into thinking it wrote a robust test suite when it hasn't.
//!
//! ### Mutation testing vs. coverage and fuzzing
//!
//! Mutation testing, code coverage, and fuzzing target different dimensions of software quality:
//!
//! * **Code coverage** measures which statements and branches execute during a test run. It identifies
//!   untested paths, but cannot reveal whether existing assertions verify the correctness of executed code.
//!
//! * **Fuzzing** generates pseudo-random inputs to discover panics, memory safety issues, or invariant
//!   violations in unmutated code under unexpected conditions.
//!
//! * **Mutation testing** introduces deliberate semantic bugs into the source code to assess whether your
//!   test suite's assertions detect logic errors.
//!
//! High code coverage is a prerequisite for high mutation scores. Mutation testing evaluates the
//! sensitivity of your tests rather than just their reach.
//!
//! ## Why `cargo-gamma`?
//!
//! `cargo-gamma` brings fast, comprehensive mutation testing to Rust projects of all sizes. There are other
//! mutation testing solutions for Rust, so why use `cargo-gamma`?
//!
//! * **Speed.** Traditional mutation testers rebuild your crate for every single mutant, which can take hours or
//!   days for large workspaces. `cargo-gamma` compiles every mutant into a single instrumented test binary and activates them
//!   individually at runtime, turning multi-minute rebuilds into fast process launches. Finally, it remembers
//!   what it learned while running to accelerate subsequent runs and CI workflows with check-in-ready hints.
//! * **Thoroughness.** `cargo-gamma` tests 146 mutator transforms across 31 families. It catches off-by-one
//!   errors (`<` vs `<=`), inverted conditions, arithmetic typos (`+` vs `-`), dropped statements, match
//!   guard errors, and literal tweaks.
//! * **Seamless workflow.** Works with your standard `cargo test` suite or `cargo-nextest`, provides built-in
//!   GitHub Actions annotations, and outputs rich interactive HTML reports and SARIF diagnostics.
//!
//! Why is it called `cargo-gamma` you might ask? Because [gamma radiation](https://en.wikipedia.org/wiki/Gamma_ray)
//! induces genetic mutations (ref [The Hulk](https://en.wikipedia.org/wiki/hulk)).
//!
//! ## Getting started
//!
//! To get going, install the tool:
//!
//! ```bash
//! cargo install cargo-gamma
//! ```
//!
//! Once installed, go to your favorite Rust workspace and enter:
//!
//! ```bash
//! cargo gamma
//! ```
//!
//! This will automatically rebuild your codebase with a bunch of mutations applied (don't worry, it doesn't
//! actually change your original source code as all the work `cargo-gamma` does happens on copied state in a cache
//! directory). Once your codebase is rebuilt, your test suite is executed repeatedly, each run with a
//! different code mutation enabled.
//!
//! If it's the first time you run mutation testing on your codebase, you'll probably see a bunch of messages
//! such as:
//!
//! ```text
//!     SURVIVED src/ledger.rs:41:9: delete self.audit.push(entry); [stmt.delete_call]
//! ```
//!
//! These messages tell you that a specific mutation was applied to your source code which was not detected by
//! your test suite. In other words, it points out a weakness in your test suite.
//!
//! Once you know what all the surviving mutants are in your codebase, your next task is to add more tests to
//! your test suite to kill those mutants.
//!
//! After adding those tests, use the completed run's JSON report to check only the mutants that
//! genuinely survived:
//!
//! ```bash
//! cargo gamma run --only-survivors
//! ```
//!
//! `--only-survivors` reads `gamma-report.json` from the artifact directory. Mutant identities remain
//! stable when only tests change. Timeout and memory-limit outcomes are not selected, even though
//! the report format represents them as survived.
//!
//! To select an exact mutant from the current source population without consulting that report:
//!
//! ```bash
//! cargo gamma run --mutant c6d3802ceceb
//! ```
//!
//! ## Mutators
//!
//! `cargo-gamma` supports a large set of mutators. These are selected to represent real-world errors that can
//! emerge in a codebase and should ideally be detected by a codebase's test suites.
//!
//! [Mutators](docs/MUTATORS.md) is the full reference of all mutators, with a table per family, every
//! mutator's academic alias, and a note on what the mutator catalog deliberately omits and why.
//! `cargo gamma list mutators` prints the same thing resolved against your current selection.
//!
//! A [mutator preset](docs/MUTATORS.md#mutator-presets) groups the catalog by what a mutant disturbs. For example,
//! `@control` includes all the mutators that change which code runs rather than what it computes, and `@numeric`
//! is literal replacement and expression perturbation.
//!
//! The `@default` mutator selection enables the main catalog; additional low-yield mutators live in `@pedantic` and are
//! not enabled by default. Ordinary runs also skip a few forms that frequently fail to compile when the
//! source does not reveal enough type information. A non-default selector that includes the mutator —
//! its name, family, another preset, or `all` — includes those forms.
//!
//! ```bash
//! cargo gamma run --mutators @control --file src/dispatch.rs
//! cargo gamma run --mutators @default,@pedantic
//! ```
//!
//! ## Safety and test side effects
//!
//! `cargo-gamma` injects machine-generated mutations to simulate bugs. If your test suite performs
//! destructive operations (such as deleting files, modifying shared databases, making network requests, or
//! communicating with external APIs), a mutated condition or path could lead to unintended side effects (for
//! example, altering path resolution during cleanup routines).
//!
//! To ensure safe and reliable execution:
//!
//! * **Use isolated test environments.** Run mutation testing inside containers, virtual machines, or disposable
//!   CI workers.
//! * **Keep tests hermetic.** Restrict file I/O to temporary directories created per test and avoid production
//!   credentials or live network endpoints in test suites.
//! * **Process isolation.** If tests share mutable global state or process resources, use `--nextest`
//!   to execute tests in dedicated processes.
//!
//! ## Run phases
//!
//! If you have a big codebase and a thorough test suite, mutation testing can take a long time (hours or days).
//! `cargo-gamma` implements a large number of optimizations to minimize this. Let's explore how `cargo-gamma` does its
//! work to start understanding these optimizations.
//!
//! A run moves through these phases, in order:
//!
//! ```mermaid
//! flowchart LR
//!     D[1. Analysis<br/><i>Find mutation sites</i>] --> I[2. Mutation<br/><i>Inject mutations</i>]
//!     I --> B[3. Building<br/><i>Compile test binaries</i>]
//!     B --> M[4. Baselining<br/><i>Verify green & measure</i>]
//!     M --> T[5. Testing<br/><i>Run test suite per mutant</i>]
//! ```
//!
//! | Phase | What happens | What it costs |
//! |---|---|---|
//! | **Analysis** | Resolve configuration, Cargo metadata, packages, targets, and source scope. | Seconds |
//! | **Mutation** | Copy workspace to temporary location and introduce<br>all mutations. | Seconds |
//! | **Building** | Check the instrumented tree, exclude unviable mutants,<br>then generate the test binaries. | Seconds or minutes |
//! | **Baselining** | Run unmutated suite to verify baseline<br>duration and peak memory. | One suite run |
//! | **Testing** | Test in parallel, stopping at<br>first test failure. | N test runs — where all<br>the time goes |
//!
//! The Building phase checks the complete instrumented schema and may take longer than a normal
//! debug build of your workspace/crate,
//! the Baselining phase will take a little longer than one run of your test suite. And the Testing phase
//! will take (number-of-mutants * time-it-takes-to-run-your-test-suite / N) where N varies based on the test suite.
//! In an idealized setting, N is around 2. So if you have a test suite that takes 1/2 hour to run and you have
//! 10,000 mutants, you can see how this would take a while.
//!
//! As it runs, `cargo-gamma` learns about your codebase and keeps track of how it could improve end-to-end mutation testing
//! performance in the future. You can capture this knowledge as *hints* that get saved in the repo and then
//! used by subsequent mutation testing runs by individuals or by the CI system. More on hints and mutation testing
//! performance below.
//!
//! ### Conditional compilation
//!
//! Mutation sites are found by reading source, so they can be located in code the build is not going to compile —
//! a module behind a `#[cfg(feature = ...)]` whose feature is off, or one behind `#[cfg(windows)]` on
//! Linux. Such a mutant cannot change what any test observes, so it would otherwise survive every run
//! and be reported as `survived` with no practical way to kill it.
//!
//! As part of the baseline build, `cargo-gamma` leverages the compiler's evaluation to identify the
//! set of mutation sites which are not actually being built, and those are marked as `not built` and
//! removed from the score computation. Deferring to the compiler rather than re-evaluating `#[cfg]`
//! predicates means this covers features, target platform and everything else cargo and rustc already
//! agreed on, with no second opinion to disagree with the first.
//!
//! You can use the `--all-features` and `--features` flags to control which feature is targeted during
//! mutation testing. `--all-features` is usually the right choice for most scenarios to ensure a codebase
//! is robust.
//!
//! Two practical limits are worth knowing:
//!
//! * **`cfg_attr` is not expanded:** `cargo-gamma` evaluates direct `#[cfg(...)]` attributes, but does not expand conditional attributes like `#[cfg_attr(...)]`. For example, a function marked `#[cfg_attr(windows, inline)]` is always mutated regardless of the target platform because `cfg_attr` conditionally applies inner attributes rather than conditionally removing the item itself.
//!
//! * **Undecidable `cfg` predicates are kept, not dropped:** If a `#[cfg(...)]` predicate references a custom flag or syntax that the build configuration cannot definitively resolve (such as `#[cfg(custom_flag)]` when no `--cfg custom_flag` is declared, or compiler-internal predicates like `#[cfg(version("..."))]`), `cargo-gamma` treats the condition as unknown rather than assuming it is `false`. The item remains in the mutation candidate population; if it turns out not to compile during the build, its mutants are cleanly withdrawn as `unviable` rather than skewing the score.
//!
//! ### Doctests
//!
//! Documentation tests are not built, run, timed or reported. `cargo test --doc` is never invoked, so a mutant
//! whose only coverage is an example in a doc comment is reported `uncovered`, and one that a doctest
//! would have caught is reported `survived`.
//!
//! This is a deliberate cost decision rather than an oversight. The schema exists so that a mutant
//! costs one test-process launch instead of one rebuild; `rustc` compiles and links a separate binary
//! per doctest, so admitting them would reintroduce a per-mutant compile for exactly the tests that
//! tend to assert least about behavior. A crate whose real coverage lives in its doc examples is
//! better served by promoting those examples into `#[test]` functions, which makes them faster for
//! `cargo test` as well.
//!
//! The consequence to keep in mind when reading a report: on a crate that leans on doctests, the score
//! is a lower bound, and the `uncovered` count is the place the difference shows up.
//!
//! ### Scoping a run
//!
//! You can choose which packages get mutated, and which packages' tests are used while testing:
//!
//! ```bash
//! cargo gamma run -p ledger -p ledger-core   # mutate only these packages
//! cargo gamma run --test-package ledger      # let this package's tests judge a mutant too
//! cargo gamma run --exclude-test conformance # keep a test target out of the oracle
//! cargo gamma run --test-workspace           # let every package's tests judge a mutant
//! cargo gamma run --test-lib                 # use only library unit-test harnesses
//! cargo gamma list mutants --re one_line    # preview mutants in this function
//! cargo gamma run --re one_line             # test only matching mutants
//! ```
//!
//! `--re` matches the full name shown by `cargo gamma list mutants`: workspace-relative file,
//! line and column, qualified function path, change description, and any suppression marker. It uses a Rust regular
//! expression, matches substrings by default, and may be repeated to include matches from any
//! pattern. Control characters use the visible escapes printed by the listing; a regular expression
//! needs `\\` to match a displayed backslash. Use `list mutants --re ...` to preview the exact
//! population before a run.
//!
//! By default, each mutant is judged by tests from its own package. That applies both to a run inside
//! `ledger-core` and to a whole-workspace run containing it: crates that depend on `ledger-core` are
//! not consulted for its mutants. This keeps the cost from becoming a function of the
//! reverse-dependency graph.
//!
//! It also keeps the score a property of the crate. Judging a library by everything that links it means
//! it scores well because some dependent happens to exercise it — and a refactor over in that dependent
//! silently withdraws the coverage with nothing to report it.
//!
//! Nothing is overstated by the cap. A mutant no admitted test binary reaches is reported as
//! `uncovered`, not as a survivor: the run says *nothing tests this*, which is true, rather than *your
//! tests missed this*, which would not be.
//!
//! To change the oracle, ask for it. `--test-package` names the packages whose tests should judge
//! instead of using each mutant's own package, which is what a workspace that keeps its integration
//! tests in a package of their own wants; if a run finds no test at all reaching the mutated code, it
//! names the packages that could and suggests the flag.
//! `--test-workspace` lets every package's tests decide, which is the most thorough oracle available
//! and costs a workspace-wide build and test run per mutant. It lifts the cap, not reachability: a
//! mutant no test binary links is still `uncovered` rather than a survivor.
//! `--test-lib` instead constrains the target kind: Cargo compiles and runs only library unit-test
//! harnesses, never integration, binary, example, or benchmark targets.
//!
//! Within the cap, reachability is still an optimization: a test binary that cannot link the mutated
//! code is never built or run.
//!
//! `--test-package` works at package granularity, which is too coarse when the tests you want as an
//! oracle and the tests you do not share a package. `--include-test` and `--exclude-test` match cargo
//! *target* names with `*` and `?` globs, which is the finest granularity cargo offers: a package's
//! unit tests take the name of the lib or bin they live in, and each file under `tests/` is a target
//! named after the file. Exclusion is applied last, so `--include-test "*" --exclude-test "conformance_*"`
//! means what it looks like.
//!
//! The usual reason to reach for this is a corpus that is not an oracle at all — conformance suites,
//! fuzz seeds, golden-file comparisons — sitting in the same package as the tests that are. Those
//! target failures say nothing about whether a mutant was noticed, and letting them convict inflates
//! the score.
//!
//! ### Differential mutation testing
//!
//! Restrict the mutation sites to the lines a change touches, which dramatically speeds up
//! the testing phase when used to handle pull requests in a CI environment:
//!
//! ```bash
//! git diff origin/main | cargo gamma run --in-diff -
//! cargo gamma run --in-diff change.patch
//! ```
//!
//! #### Compile-fail targets
//!
//! A `trybuild` or `compiletest_rs` target is an expensive special case, and a run says so before it
//! builds anything:
//!
//! ```text
//! warning 1 test target invokes the compiler once per case, so every mutant pays for a full rustc run
//!         and almost none are convicted by it: `router_compile_fail` in routerama (trybuild).
//!         If it is not part of what should be judging these mutants, exclude it with
//!         `--exclude-test router_compile_fail`.
//!         Left in, it is likely to make this run take hours.
//! ```
//!
//! The cost is real: such a target has been measured putting a single mutant at one to two minutes,
//! which turns a run of a few thousand mutants into a multi-day one. What it asserts is that some code
//! *fails* to compile, and a mutated function body in another crate almost never changes that — so
//! nearly every mutant pays the full price and is convicted by something else anyway.
//!
//! Nothing is excluded for you. On a proc-macro crate `trybuild` is often the *primary* oracle: a
//! mutant that corrupts a diagnostic message is caught there and nowhere else, so excluding it by
//! default would gut the oracle for exactly the code the technique suits best, and the mutants would
//! come back as survivors rather than as anything visibly wrong. Whether the catch rate justifies the
//! price is yours to weigh, so the run states the price and the flag and leaves the decision alone.
//!
//! ### Controlling a run
//!
//! ```bash
//! cargo gamma run --jobs 8                      # mutants tested in parallel
//! cargo gamma run --test-timeout-multiplier 2.0 # multiple of each test binary's baseline duration
//! cargo gamma run --minimum-test-timeout 5      # floor under the computed budget
//! cargo gamma run --build-timeout 600           # bound each compiler invocation
//! cargo gamma run --min-score 80                # fail the run below a score
//! cargo gamma run --max-flaky 0                 # reject flaky nextest enumeration-control outcomes
//! cargo gamma run --dry-run                     # report the plan without building anything
//! cargo gamma run --show-killed                 # list what the suite killed, not just what survived
//! cargo gamma run --show-unviable               # list the mutants that could not compile
//! cargo gamma run --leak-dirs                   # keep incomplete scratch trees for inspection
//! cargo gamma run --cache-dir /fast/disk        # put reusable workspace and build state here
//! cargo gamma run --artifact-dir reports        # put all five user-facing artifacts here
//! cargo gamma clean                             # delete this workspace's reusable cache
//! cargo gamma run --incremental build           # default: reuse ordinary compiler outcomes and checked hints
//! cargo gamma run --incremental no              # no incremental reuse; compiler exclusions still apply
//! ```
//!
//! A run computes each mutant's budget from the unmutated suite, so a fast suite gets a tight one.
//! `--minimum-test-timeout` stops a loaded machine from reporting scheduling noise as a hang.
//! Compiler work consists of convergence checks, bounded proof checks when attribution needs them,
//! and one final code-generating build. `--build-timeout` and `--build-timeout-multiplier` bound
//! each invocation, and an invocation that outstays its budget is stopped with its Cargo stage named.
//!
//! The campaign establishes a compiler-viable mutant schema before any mutant is executed.
//! `cargo check` examines the instrumented packages together and compiler-rejected mutants are
//! withdrawn until the schema checks; a code-generating build then produces the converged test
//! binaries. Packages without library targets work both on their own and alongside library packages
//! in a workspace run. A code-generation or linking failure uses the same bounded convergence rules.
//! `--build-timeout` bounds each compiler invocation, while
//! `--build-timeout-multiplier` bounds later convergence rounds relative to the first.
//!
//! The workspace is copied to `workspace/` under cargo-gamma's external scratch area before
//! anything is rewritten. By default, Cargo artifacts and campaign state live under
//! `<resolved-target>/cargo-gamma/cache/<workspace-id>/`, so repeated runs compile incrementally,
//! shared Cargo target directories keep workspaces separate, and `cargo clean` can reclaim the
//! potentially large build cache. `--cache-dir` retains the all-in-one layout and moves the
//! synchronized workspace, build artifacts, and campaign state together. The path names the cache
//! itself and must be empty on first use; cargo-gamma marks it as belonging to that workspace and
//! refuses to let another workspace adopt or share it. A workspace-specific lock in cargo-gamma's
//! default external scratch area serializes commands targeting the same original workspace, while
//! a second lock protects redirected cache state.
//! To move only compiler artifacts while preserving checkout-visible Git metadata for build scripts,
//! leave `--cache-dir` unset and use Cargo's `CARGO_TARGET_DIR` or `build.target-dir`.
//!
//! `cargo gamma clean` deletes the workspace-specific external scratch and target-resident campaign
//! cache after taking the same lock as a run. It leaves published reports under
//! `target/cargo-gamma`, checked-in hints and source suppressions untouched.
//!
//! As soon as a fresh measured run acquires its campaign cache, it truncates `gamma-progress.log` and
//! `gamma-selection.jsonl`. Testing-only resume preserves these logs without replaying their previous
//! verdict lines. Every new mutant verdict is appended and flushed to the progress journal as
//! it arrives, using the console's outcome-line format but without color or redraw escapes. Unlike
//! the console, the journal includes ordinary killed mutants as well as survivors, timeouts, memory
//! exhaustion, flakes and uncovered mutants. The JSON Lines selection journal is transient,
//! non-verdict scheduling telemetry; its fields are explained with the test-selection model below.
//! An interrupted run therefore leaves every complete verdict and the retained prefix of complete
//! selection attempts available for recovery and heuristic analysis, even though its final reports
//! were never written.
//!
//! What gets copied follows version control: files git tracks are always copied, whatever an ignore
//! rule says about them. `--copy-ignored` adds the untracked ones, and is for the build that reads
//! something a shared `.gitignore` excludes — a generated module, a downloaded fixture — which would
//! otherwise have to be fixed by editing an ignore file that is not this tool's to edit. It copies
//! everything else the tree has ever built too, so expect a run with it on to be slower to start.
//!
//! Reports normally go to `target/cargo-gamma` in the original workspace. `--artifact-dir` moves
//! all five together and creates the destination if needed; `--cache-dir` never changes them.
//! Report source is read from the campaign's retained synchronized snapshot and checked against
//! discovery digests, while file keys, locations, and `projectRoot` continue to name the original
//! workspace. Editing or deleting the original checkout after discovery therefore cannot mix source
//! generations into a completed report.
//!
//! A completed measured campaign writes its complete outcome ledger to `last-gamma-run.json` in
//! the cache. With incremental reuse enabled, that atomic record also carries score-neutral
//! scheduling knowledge. If an incremental campaign fails part-way through, it leaves the completed
//! ledger untouched and writes only its best-effort scheduling knowledge to
//! `incomplete-gamma-learning.json`; the next completed campaign consumes and clears that separate
//! cache. Dry runs write neither file, and `--incremental no` does not read or write incomplete
//! learning. A repeated incremental run reads the completed record and incomplete learning, so it
//! avoids rediscovering compiler unviability and tries the previous killer before inferred tests.
//! Killer tests are hints, not cached verdicts: every score-bearing mutant is executed again.
//! Ordinary cached unviability requires the same compiler, Cargo configuration, build policy,
//! and unchanged compilation inputs. Independently, `compiler-exclusions.json` in the campaign
//! cache excludes exact mutant IDs across context changes and even with `--incremental no`.
//! To reconsider a repaired mutant, remove its inventory entry and use `--incremental no`;
//! deleting the whole inventory can import old compiler failures again. See
//! [Compiler exclusions](docs/CMDLINE.md#compiler-exclusions) and
//! [What a run remembers](#what-a-run-remembers). `cargo gamma hints` promotes
//! the parts that cannot move a score directly from that record,
//! after using Cargo metadata to validate the current workspace identity but without source
//! discovery, into `gamma-hints.yaml`, which you can commit so a fresh CI container starts warm.
//!
//! Control how the tree is compiled and how the tests are invoked:
//!
//! ```bash
//! cargo gamma run --all-features
//! cargo gamma run --features serde,rayon --no-default-features
//! cargo gamma run --profile gamma           # use an optimized profile defined in Cargo.toml
//! cargo gamma run --cargo-arg --offline
//! cargo gamma run --cargo-test-arg --skip --cargo-test-arg slow_
//! cargo gamma run -- --skip slow_           # the same thing, for arguments that need no escaping
//! cargo gamma run --nextest                 # a process per test, for suites that need the isolation
//! ```
//!
//! An optimized profile can be the right trade because code generation is normally paid once for the
//! converged binaries and thousands of mutants run against them. Avoid using `release` without
//! considering its semantics: it normally turns
//! `debug_assertions` and overflow checks off, so mutants those checks would have caught may survive
//! instead. [Optimizing compute-heavy suites](#optimizing-compute-heavy-suites) defines a safer
//! opt-in profile and explains when it pays.
//!
//! Three flags pass arguments through, and they go to two different places. `--cargo-arg` is handed to
//! **cargo**, so it controls the build: `--offline`, `--locked`, `--frozen` and anything else cargo
//! understands. `--cargo-test-arg` and everything after `--` are handed to **every test binary**, so
//! they control the harness: `--skip`, `--test-threads`, `--nocapture`. The last two are the same
//! destination and are concatenated in the order written, so pick whichever reads better — `--` is
//! shorter and needs no escaping, while `--cargo-test-arg` can be interleaved with other flags,
//! survives being appended to by a wrapper script, and is the form the
//! [`cargo-test-args`](docs/CONFIG.md) configuration key takes, which a file cannot write as a trailing
//! `--`.
//!
//! A mutant is usually caught by one test, so a binary is stopped at the first test that announces a
//! failure rather than run to the end — on a suite whose slow tests come after a fast one, that is most
//! of the run. This relies on the harness being the only thing writing to the output being read, which
//! is true because libtest captures each test's output and replays it only once every test has
//! finished. Passing `--nocapture` or `--show-output` (or setting `RUST_TEST_NOCAPTURE`) breaks that,
//! since a test's own writing then lands among the harness's and could be mistaken for a verdict, so
//! the run falls back to reading each binary to completion. Scores are identical either way; only the
//! time differs.
//!
//! By default the built test binaries are executed directly, which is where much of the speed comes
//! from. Some suites cannot pass that way: a test that mutates the environment, leans on a global, or
//! installs a process-wide handler only holds when it has a process to itself. Under the default runner
//! such a suite is red before any mutant is applied, and a red baseline stops the run — there is
//! nothing to measure against. `--nextest` gives each test its own process, which makes those
//! trees measurable at all.
//!
//! It costs less than it appears to. Nextest is handed the already-built tree and never invokes cargo,
//! so it adds no compiler work, and budgets, ordering, timeouts and memory accounting work as they do
//! otherwise. It does need `cargo-nextest` on the path. The two runners agree on the verdicts for any
//! suite that passes under both.
//!
//! Point the run somewhere other than the current directory, or say explicitly that the whole workspace
//! is in scope:
//!
//! ```bash
//! cargo gamma run --dir ../ledger    # analyze a workspace elsewhere
//! cargo gamma run --workspace        # every package: accepted for symmetry with cargo, already the default
//! ```
//!
//! Reach an error type that does not implement `Default`:
//!
//! ```bash
//! cargo gamma run --error 'MyError::Io' --error 'MyError::Eof'
//! ```
//!
//! Each value becomes its own `fn_value.err_with` mutant on every function returning a `Result`.
//!
//! By default each test binary gets a budget derived from how long that binary took with no mutant active, because a
//! mutant that turns a loop bound into an infinite loop should be cut off quickly. `--test-timeout-multiplier` changes how much
//! slower than the baseline a mutant may run in that binary before it is called a timeout, and `--minimum-test-timeout` provides a floor under the budget.
//!
//! Some mutants cannot compile — replacing a body with `Some(Default::default())` only works when the
//! type implements `Default`. cargo-gamma checks the complete instrumented schema first, withdraws
//! compiler-rejected mutants, and checks again. The converged test binaries are generated only after
//! these rounds finish. Withdrawn mutants are reported as unviable rather than counted against the
//! score. Convergence is iterative because rustc reports only the errors it reaches before it gives
//! up, so a large tree can need several rounds.
//! `--rollback-rounds` raises the cap; raise it when a run stops with a rollback-limit error and the
//! withdrawal counts it is printing are still falling.
//!
//! Two flags control what the terminal sees. Both take `auto`, `always` or `never`, and both default to
//! `auto`, which means "on when standard error is a terminal":
//!
//! ```bash
//! cargo gamma run --color never --progress never   # what a CI log wants
//! ```
//!
//! `--progress` governs the live counter, which is redrawn in place and therefore turns a CI log into
//! thousands of near-identical lines when nothing is there to interpret the escapes. `--color` governs
//! styling alone, so a log can stay colorless without losing the progress the flag would otherwise
//! suppress.
//!
//! Compiler convergence can invoke Cargo several times with different unit graphs. Cargo's `X/Y`
//! counter therefore resets and does not measure progress toward a viable schema. The normal display
//! hides it and instead reports a monotonic `Excluding unviable mutants (N found)` phase, followed by
//! the final unviable and viable counts. `--show-build` exposes Cargo's raw narration for
//! troubleshooting:
//!
//! ```bash
//! cargo gamma run --show-build
//! ```
//!
//! Every test process a run launches, baseline included, has `CARGO_GAMMA=1` set. A suite that drives
//! cargo itself needs this: a nested build inside the scratch tree fails for reasons unrelated to any
//! mutant, and that shows up as a red baseline before a single mutant runs.
//!
//! Be aware of what stepping aside costs. A test that returns early on `CARGO_GAMMA` is not deciding
//! anything, so every mutant only that test could have caught is reported as a survivor rather than as
//! uncovered — the score falls, and the reason is invisible. Prefer moving cargo-driving tests into a
//! test target of their own and naming it to [`--exclude-test`](docs/CMDLINE.md#full-option-reference): the run
//! then knows those tests were withheld and says so in the oracle note, instead of silently crediting
//! their absence to your code.
//!
//! ## Verdicts and the score
//!
//! Every mutant ends in a verdict classified as detected, undetected, or excluded from scoring:
//!
//! ```mermaid
//! flowchart TD
//!     Start(["Mutant candidate"]) --> Filter{"Active in run?"}
//!     Filter -->|Suppressed or out of shard| Ignored["Ignored / not built<br/>(excluded)"]
//!     Filter -->|Yes| Compiles{"Compiles?"}
//!     Compiles -->|No| Unviable["Unviable<br/>(excluded)"]
//!     Compiles -->|Yes| Reach{"Known unreachable?"}
//!     Reach -->|Yes| Uncovered["Uncovered<br/>(undetected)"]
//!     Reach -->|No| TestRun["Execute test suite"]
//!     TestRun --> Outcome{"Test outcome"}
//!     Outcome -->|Test fails| Killed["Killed<br/>(detected)"]
//!     Outcome -->|Hangs or stalls| Timeout["Timed out<br/>(undetected)"]
//!     Outcome -->|Exceeds memory| OutOfMem["Out of memory<br/>(undetected)"]
//!     Outcome -->|All tests pass| Survived["Survived<br/>(undetected)"]
//! ```
//!
//! | Verdict | In the report | Meaning | In the score |
//! |---|---|---|---|
//! | Killed | `killed` | A test failed while this mutant was active | detected |
//! | Timed out | `timeout` | The budget or the stall detector cut it off before an assertion rejected it | **undetected** |
//! | Out of memory | `outofmem` | It passed the memory ceiling before an assertion rejected it | **undetected** |
//! | Survived | `survived` | Every reachable test passed | **undetected** |
//! | Uncovered | `uncovered` | No test in scope reached this mutation site | **undetected** |
//! | Unviable | `unviable` | The mutation does not compile | excluded |
//! | Ignored | `ignored` | Withdrawn by a directive, attribute or config rule | excluded |
//! | Not built | `notbuilt` | Conditional compilation kept this file out of the build | excluded |
//! | Flaky | `flaky` | An unmutated nextest enumeration control failed or was inconclusive,<br>or a historical report recorded a flaky outcome | excluded |
//! | Not run | `pending` | Never judged — outside shard, settled by<br>earlier report, run ended first, or the machine<br>refused to start the test binary | excluded |
//!
//! The middle column is the name a JSON report carries and a log can be grepped for; the console uses
//! the same words.
//!
//! ### How the mutation score is calculated
//!
//! The **mutation score** is detected over detected-plus-undetected:
//!
//! ```text
//!                              killed
//! score =  ─────────────────────────────────────────────────────────  × 100
//!          killed + timed out + out of memory + survived + uncovered
//! ```
//!
//! Unviable, ignored, never-built, flaky and unrun mutants are in neither half. They were never really
//! tested, and counting them either way would be a statement about your tests that your tests did not
//! make.
//!
//! Only a failing assertion increases the numerator. Timeout, out-of-memory, survivor, and
//! uncovered verdicts increase only the denominator. An excluded verdict changes neither. If
//! there are no scored mutants at all, the displayed ratio is 100%, but `--min-score` treats the
//! run as ungraded rather than allowing an empty population to pass a quality gate.
//! An `--in-diff` run that selects no mutants is a successful no-op, even with score and flaky gates.
//! `--max-flaky` separately gates recorded flaky outcomes. Ordinary test failures are not rerun
//! without the mutant, so this gate does not detect intermittent assertion failures after baseline.
//!
//! Uncovered mutants are in the denominator and never in the numerator, so they cost you exactly what
//! a survivor costs you. That is deliberate. *Which* mutants went undetected is a diagnosis, and "no
//! test reaches this code" is a different problem from "the tests that ran it did not notice" — so the
//! report never merges them. But *how much* of the code is defended is one number, and code no test
//! reaches is undefended.
//!
//! The [mutation-testing-elements](https://github.com/stryker-mutator/mutation-testing-elements)
//! schema treats its `Timeout` status as detected. Gamma exports timeout and out-of-memory
//! verdicts as schema `Survived` with a reason that preserves the actual outcome, so the standard
//! report UI computes this same fail-closed score.
//!
//! ### Why 100% line coverage can still have uncovered mutants
//!
//! Line coverage and gamma's `uncovered` verdict need not measure the same population. Coverage tools
//! can deliberately remove code from their denominator with Rust's `#[coverage(off)]` attribute (often
//! written conditionally as `#[cfg_attr(coverage_nightly, coverage(off))]`). Gamma still mutates that
//! production code unless it is explicitly suppressed, so a mutation guard there can be reached by no
//! selected test while the coverage report remains at 100%.
//!
//! Other legitimate mismatches have the same shape:
//!
//! * a coverage service may combine operating systems, feature sets or test targets that are absent
//!   from this gamma run;
//! * cfg-disabled code and proc-macro or build-time code may compile without executing during gamma's
//!   runtime test execution;
//! * line coverage can mark a line even though a particular expression or branch on that line did not
//!   execute; and
//! * doctests are not part of gamma's runtime oracle.
//!
//! `Uncovered` therefore means exactly that no selected runtime test activated this mutation site's
//! gamma guard **in this run**. It does not claim that every coverage upload omitted the source line.
//! The remedy depends on intent: add a runtime test, align gamma's platform/features/test-targets with
//! the coverage job, or [suppress the mutant](#suppressing-mutations) when it is intentionally outside
//! the oracle (for example unstable `Debug` text). A coverage exclusion is not automatically a mutation
//! exclusion: deliberately uncounted code can still contain behavior worth testing.
//!
//! ### Why some mutants cannot be killed
//!
//! Some mutations produce a program that behaves identically to the original. Swapping `<` for `<=` on
//! a bound nothing ever reaches, changing a value that is overwritten before it is read, altering a
//! log message, reordering two independent operations — the code is different and the behavior is not.
//! These are **equivalent mutants**, and no test can kill one, because there is nothing to observe.
//!
//! There are two general approaches for dealing with unkillable mutants:
//!
//! * When a survivor is genuinely unkillable, say so in the source with a
//!   [suppression directive](#suppressing-mutations) — `#[gamma::skip(reason = "…")]` — which is
//!   reviewable and survives a clean checkout. Prefer that over widening an exclusion.
//! * When a whole mutator produces mostly equivalent mutants in your code, drop it for that tree with
//!   [`--mutators`](#mutators) or the `mutators` key in [configuration](#configuration).
//!
//! ### Acting on surviving mutants
//!
//! A surviving mutant is an observation about your test suite. When triaging survivors:
//!
//! 1. **Prioritize high-impact code:** Focus first on core business logic, public APIs, boundary
//!    conditions, and safety-critical invariants rather than non-functional code (like debug logging).
//! 2. **Cluster by function or module:** Multiple survivors in the same function usually point to a
//!    single missing test scenario rather than multiple independent test gaps.
//! 3. **Prefer public contract assertions:** Write tests against the public API to verify observable
//!    behavior rather than writing tests that tightly couple to internal private implementation details.
//! 4. **Distinguish non-functional and cache code:** If a survivor alters an internal cache, memoization,
//!    or logging path that produces identical functional results, test its observable telemetry or suppress
//!    it explicitly with `#[gamma::skip(reason = "performance cache")]`.
//!
//! ## Killing mutants
//!
//! ### Fixing timeouts, out-of-memory and unviable mutants
//!
//! Some sites cannot usefully be mutated — a hand-written spin loop, a driver poll, a reactor.
//! `suppress` promotes outcomes from the latest completed campaign and writes the suppression
//! without workspace synchronization, rebuilding, baselining, or rerunning tests. A valid
//! persisted workspace locator avoids Cargo metadata; without one, or whenever `--cache-dir` is
//! explicit, the command uses metadata to resolve and validate the selected workspace.
//! It validates only affected source files against the persisted site identity and leaves both
//! `last-gamma-run.json` and `gamma-progress.log` unchanged. Like `unsuppress`, it previews by
//! default and requires `--apply` to edit source; the inherited run flag `--dry-run` is rejected:
//!
//! ```bash
//! cargo gamma suppress                                      # print the diff, change nothing
//! cargo gamma suppress --apply                              # timeouts and out-of-memory mutants
//! cargo gamma suppress --apply --eligible timeout           # timeouts only
//! cargo gamma suppress --apply --eligible timeout,outofmem,unviable
//! ```
//!
//! The default covers both `timeout` and `outofmem`, because both are a mutant the machine stopped
//! rather than a mutant the suite convicted, and both cost a full budget every run to reach the same
//! answer. A run that produces either verdict prints a hint pointing to this command. `unviable`
//! stays opt-in: a mutant that will not compile is cheap to re-establish, and suppressing it hides a
//! mutator that may simply need narrowing.
//!
//! A site stopped for more than one reason gets one directive per reason, each naming the verdict that
//! actually applied, so a later reader can tell which mutators hung from which ones exhausted memory.
//!
//! **A surviving mutant is never eligible, and cannot be made eligible.** A survivor is a real gap in
//! the test suite; suppressing it would remove the gap from the score rather than from the code, and
//! the moment that is possible every score the tool reports becomes unfalsifiable.
//!
//! Generated directives name the exact mutators that tripped — never a family, never `all` — and carry
//! a tag, a reason and the date, so they can be audited later. After writing, discovery runs again and
//! the suppressed set is compared in both directions: every intended mutant must now be suppressed, and
//! nothing else may have become suppressed. If either check fails, every edit is reverted.
//!
//! The result is a baseline that a later run can be compared against: anything new is a regression
//! introduced by the change under review. Review the diff before committing it — a bulk suppression is
//! a snapshot of what one run could not resolve on one day, not a judgment that any of it should stay
//! that way.
//!
//! #### Taking suppressions back out
//!
//! A suppression that has stopped earning its place can be removed the same way it was written:
//!
//! ```bash
//! cargo gamma unsuppress            # print the diff, change nothing
//! cargo gamma unsuppress --apply    # remove them
//! ```
//!
//! It removes exactly the directives a run reports as suppressing nothing, and the preview is the
//! default for both suppression commands. A directive written in error can be read back and
//! reverted at leisure, while one deleted in error takes its reason with it.
//!
//! Nothing is built and no test is run: whether a directive governs a mutant is a fact about the
//! source, so this costs a discovery pass. Only a directive that is the whole content of its own line
//! is removed; one attached to a line of code, wrapped in a `cfg_attr`, or spread over several lines is
//! named and left for a person, because there is no line whose deletion would take the directive and
//! nothing else. Afterwards discovery runs again and the population is compared: removing a directive
//! that suppressed nothing cannot change anything, so if anything does change, every edit is reverted.
//!
//! ### Hangs
//!
//! Deleting a statement or relaxing a loop condition makes runaway loops common, and a hung mutant is
//! the most expensive verdict a run can produce — it is the only one whose cost is decided by how long
//! you are willing to wait. Waiting out a timeout derived from a slow binary means spending significant
//! time to learn that a one-line change made a twelve-millisecond test spin forever.
//!
//! So the run does not wait. The baseline measures the longest a healthy suite legitimately goes
//! without saying anything, and a mutant that goes quiet for much longer than that is presumed hung and
//! cut off. The budget is calibrated rather than fixed, because a suite whose slowest test takes half a
//! minute goes half a minute quiet when it is perfectly healthy, and a constant would either accuse it
//! or be too loose to help a suite of millisecond unit tests.
//! The last test named by the harness is a landmark rather than a diagnosis: parallel harnesses
//! announce tests when they finish, so the test still spinning may be one the harness has not named.
//!
//! #### Bounding what a mutant allocates
//!
//! A mutation can turn bounded allocation into unbounded allocation — a loop bound inverted, a
//! capacity computed by multiplication instead of division. The timeout does eventually stop such a
//! mutant, but only after it has taken the machine into swap, and possibly after the kernel has killed
//! something that had nothing to do with the run.
//!
//! `--memory enforce` is the default, on the same reasoning as the timeout: the user who most needs
//! protecting from a runaway allocation is the one who never thought to ask for it. Each test binary's
//! whole process tree is metered during the baseline, and every mutant of that binary is then held to a
//! ceiling derived from what it measured — by default the larger of twice the baseline peak and the
//! baseline peak plus 128 MiB. The multiplier governs large suites; the headroom governs small ones,
//! where doubling a few megabytes would leave no room for a lazily initialized table.
//!
//! A mutant stopped for reaching its ceiling is reported as `OUTOFMEM` and counted in its own
//! column of the summary, with a note naming the binary, the peak it reached and the ceiling it passed:
//!
//! ```text
//! OUTOFMEM src/buffer.rs:5:15: replace steps.min(4) with steps.max(4) [iter.min_to_max]:
//!          `subject-2181f69f` reached 192.6 MB, past the 192.6 MB this run allowed it
//!
//! Summary 1 mutant (0 killed, 0 survived, 0 timed out, 1 out of memory, 0 uncovered => 0.0%)
//! ```
//!
//! It counts as undetected, like a timeout: the resource guard established that the mutant changed
//! behavior, but no test assertion rejected it. It gets its own outcome because its remedy is
//! different from a completed survivor's. It is also the outcome most likely to be wrong: a ceiling
//! set too tight can stop a healthy mutant, which is why the note carries both numbers.
//!
//! `--memory measure` meters and reports without ever stopping a mutant, which is what to use if you
//! want the numbers before you trust the ceiling. `--memory off` disables both. A mutant whose site is
//! genuinely allowed to allocate this much can be taken out of the run for good with
//! `cargo gamma suppress --apply`, which treats `outofmem` as eligible by default alongside
//! `timeout`.
//!
//! On Linux and Windows the kernel enforces the ceiling for the whole process tree. macOS has no
//! equivalent, so there cargo-gamma samples the test process group's memory every 10 ms and stops it
//! once it passes the ceiling. That bound is soft. A mutant growing very fast can overshoot by roughly
//! its growth rate times 10 ms. A process that leaves the test's process group is not counted. The run
//! says so once at the start. With `--nextest` on macOS, each test runs in a process group of its own,
//! outside what this sampling can see, so memory control is unavailable. When it was only the default,
//! it is switched off with a note. When you asked for it, the run stops and tells you to drop `--nextest`
//! or pass `--memory off`.
//!
//! #### Baseline and intermittent test failures
//!
//! Baseline runs the selected tests with no mutant active before mutation testing. After that,
//! a failing mutant execution is accepted as a kill immediately, including when a hinted test
//! catches it. There is no additional unmutated run per kill.
//!
//! A passing baseline does not prove that tests are deterministic. An intermittent assertion
//! failure after baseline can therefore be counted as a kill; cargo-gamma does not distinguish it
//! from a mutation-induced failure by rerunning that test. Fix flaky tests before relying on the
//! score. `--no-baseline` also removes the initial healthy-suite check.
//!
//! Nextest enumeration failures still receive an unmutated control run to distinguish a broken
//! test runner from a mutation-induced enumeration failure. If that control run fails a test,
//! the mutant is reported as `FLAKY`, excluded from the score, and retried in later campaigns.
//! A control that times out, stalls or exceeds its memory ceiling also occupies this score-neutral
//! bucket, with its resource failure recorded rather than a claim that the test failed twice.
//! Enumeration controls are the only remaining source of new flaky verdicts, not a general flaky-test detector.
//! Historical flaky verdicts remain readable in reports and can be gated during merging.
//! Timeout, stall and memory suspicions retain their resource-specific retries.
//!
//! `--no-confirm` and `no-confirm` are unsupported; omit them from commands and configuration.
//!
//! ## Suppressing mutations
//!
//! Not every surviving mutant is a missing test. A mutant can be *equivalent* — a program that behaves
//! identically, so no test could ever tell them apart — or it can sit in code that is deliberately
//! untested. Both cost a reviewer attention every run until somebody records the decision, and a
//! suppression is that record.
//!
//! Every mutator has a stable, well-known name of the form `family.transform`. That one name is the
//! vocabulary for the command line, the report, and every suppression channel.
//!
//! ### Choosing a channel
//!
//! The available suppression and test-selection channels differ in where the decision lives and in
//! how visible it is to the next person to read the code.
//!
//! | Channel | Scope | Where it lives | Reach for it when |
//! | --- | --- | --- | --- |
//! | `#[gamma::skip]` | item, block, statement, expression | beside code | one specific site is equivalent or untested |
//! | `// #[gamma::skip]` | the same syntax in a comment | beside code | site cannot carry an attribute or in clean source |
//! | `exclude-trait-impls` | implementations of named traits | `gamma.toml` | one cross-cutting project policy covers every matching implementation |
//! | `--mutators !fam` | the whole run | CLI or config | whole mutation class does not apply |
//! | `--exclude-file` | all mutants in matching files | CLI or config | generated, vendored, or unowned files |
//! | `--exclude-test` | tests that run, not mutants | CLI or config | slow or flaky test should not judge mutants |
//!
//! The first two are almost always the right answer. A directive next to the code says *this* mutant is
//! equivalent and says why; a config-level exclusion says nothing about any individual site and quietly
//! grows to cover code written years later that nobody ever considered.
//! A trait exclusion list is the exception for a deliberate cross-cutting policy. It compares each
//! configured identifier with the final segment of the trait path as written, without name
//! resolution, so `exclude-trait-impls = ["Debug", "Display"]` covers qualified and unqualified
//! implementations of either trait without matching report prose. Put the reason in a comment
//! beside the list, as with `exclude-files`.
//!
//! `--exclude-test` is the odd one out: it does not suppress a mutant at all, it narrows the test suite
//! each mutant is run against. Excluding a test makes mutants *harder* to kill, not easier.
//!
//! ### In-source directives
//!
//! A directive is written in the `gamma` namespace:
//!
//! ```rust
//! #[gamma::skip]                         // do not generate mutants here
//! #[gamma::expect_survived]              // mutants here are expected to survive
//! #[gamma::expect_killed]                // mutants here are expected to be killed
//! #[gamma::test_timeout_multiplier(3.0)] // override timeout multiplier for mutants here
//! ```
//!
//! Or using the generic `#[gamma]` attribute:
//!
//! ```rust
//! #[gamma(test_timeout_multiplier = 3.0)]
//! #[gamma(3.0)]
//! ```
//!
//! Anything else in the `gamma::` namespace is a usage error rather than something quietly ignored, so
//! a typo in a directive name is reported instead of silently disabling nothing. Attributes in other
//! namespaces, including `mutants::`, are left alone.
//!
//! #### Selecting what to suppress
//!
//! A bare directive covers everything:
//!
//! ```rust
//! #[gamma::skip]
//! fn hash_seed() -> u64 {
//!     0x9E37_79B9_7F4A_7C15
//! }
//! ```
//!
//! Arguments narrow it, using exactly the same grammar as `--mutators` — mutator names, family prefixes,
//! mutator presets written with `@`, and `!` negation, comma separated:
//!
//! ```rust
//! #[gamma::skip(relational)]                  // one family
//! #[gamma::skip(relational.lt_to_le)]         // one mutator
//! #[gamma::skip(@arithmetic)]                 // a profile
//! #[gamma::skip(arith, literal.int)]          // several selectors
//! #[gamma::skip(@all, !fn_value)]             // everything but one family
//! ```
//!
//! Sharing the grammar is deliberate. A selector you worked out at the command line while narrowing
//! down a result pastes straight into the source once you decide the answer is permanent.
//!
//! On an item, a directive is a real attribute:
//!
//! ```rust
//! #[gamma::skip(arith, reason = "fixed-point math, checked by proptest")]
//! fn scaled(a: i64, b: i64) -> i64 {
//!     a * b / 1000
//! }
//! ```
//!
//! `#[cfg_attr(…, gamma::skip(…))]` is honored too. The predicate is deliberately not evaluated:
//! suppression states an intent about a site, and that intent does not change with the build
//! configuration.
//!
//! #### Directive arguments
//!
//! Named arguments may follow the selectors in any position:
//!
//! ```rust
//! #[gamma::skip(
//!     literal,
//!     reason = "the seed is arbitrary; any value works",
//!     tag = "equivalent"
//! )]
//! fn hash_seed() -> u64 {
//!     0x9E37_79B9_7F4A_7C15
//! }
//!
//! #[gamma(test_timeout_multiplier = 2.5)]
//! fn slow_algorithm(data: &[u8]) -> usize {
//!     data.len() * 2
//! }
//! ```
//!
//! * `reason` is free text and appears in the report and in `cargo gamma explain`, so the next person to
//!   wonder about the gap gets the answer without a `git blame`.
//! * `tag` is a short label you choose, which lets a report be grouped by category — `equivalent`, `perf`,
//!   `unsafe` — and lets a review ask how many suppressions of a given kind a change added.
//! * `test_timeout_multiplier` overrides the timeout multiplier for the annotated mutants. When that mutant is tested, the multiplier specified in the attribute overrides the default specified in configuration or via `--test-timeout-multiplier` (scaled against the test binary's measured baseline duration and respecting `--minimum-test-timeout`).
//!   A positional multiplier may appear anywhere in the argument list, but a directive may state
//!   exactly one multiplier across the positional spelling and the named aliases
//!   `test_timeout_multiplier`, `timeout_multiplier`, `multiplier`, and `factor`. A second value is
//!   a usage error.
//!
//! Both `reason` and `tag` are optional, but a `skip` with no `reason` is a decision nobody can audit.
//!
//! #### When a suppression stops earning its place
//!
//! A run names every `skip` directive that suppressed nothing after its summary and points to the
//! applying form of `unsuppress`:
//!
//! ```text
//! Summary: 127 mutants (120 killed, 4 survived, 1 timed out, 0 out of memory, 2 uncovered => 94.5%)
//! Note   : 2 superfluous skip directives could be removed with `cargo gamma unsuppress --apply`
//!   src/parse.rs:118: skip(arith) — the compiler folds this
//!   src/hash.rs:42: skip(literal)
//! ```
//!
//! This is the one part of a report nobody can audit from the outside. Once the code under a directive
//! changes, the directive keeps applying to nothing, and it still reads as a deliberate decision that
//! something there is untestable. Naming it is what lets that decision be revisited.
//!
//! A directive is only named when this run actually had a mutant to offer it. `--mutators relational` never
//! generates an `arith` mutant, so it never condemns a `skip(arith)`; a file outside `--package`, a
//! shard that did not hold it, or an `--in-diff` that skipped it is never scanned at all. The note is
//! never a failure — a directive can be legitimately idle for one run.
//!
//! #### The comment form
//!
//! Attributes in statement and expression position are still unstable in Rust, so every directive has a
//! comment spelling, which is character-for-character the attribute with `//` in front:
//!
//! ```rust
//! // #[gamma::skip(literal, reason = "the seed is arbitrary")]
//! fn hash_seed() -> u64 {
//!     0x9E37_79B9_7F4A_7C15
//! }
//! ```
//!
//! ```rust
//! // #[gamma::skip(arith)]
//! let total = a * scale + offset;
//! ```
//!
//! It exists for the places an attribute cannot go, and for codebases that would rather not carry a
//! tool's attributes in shipped source. It behaves identically otherwise, and when expression
//! attributes stabilize, deleting the two slashes turns each of these into real Rust. This includes
//! the single-multiplier rule described above.
//!
//! ### Where a directive can go
//!
//! An attribute may be placed on any of:
//!
//! * a free function (`fn`)
//! * an inherent or trait method (`impl` / `trait` body)
//! * an `impl` block, covering everything in it
//! * a `mod`, covering everything in it
//! * a statement
//! * an expression
//!
//! A **comment on its own line** governs the outermost construct that begins after it. A **trailing
//! comment** — one following code on the same line — governs the widest span that starts on that line.
//! So this suppresses the whole `if`, not just the condition:
//!
//! ```text
//! if a < b && c > d {   // #[gamma::skip(relational)]
//! ```
//!
//! while this suppresses only the statement that follows:
//!
//! ```rust
//! // #[gamma::skip(stmt)]
//! counter += 1;
//! ```
//!
//! Placing a directive on a `mod` or an `impl` is the bluntest form: it will keep covering code added to
//! that block long after the reason it was written stops applying. Prefer the narrowest placement that
//! covers the site.
//!
//! ### Stating what a site's fate should be
//!
//! `skip` stops a mutant being generated. `expect_survived` and `expect_killed` still generate it, still
//! run it, and turn the result into an assertion — they are claims about the suite rather than
//! instructions to the generator. If the outcome disagrees with the claim, the run reports each
//! divergence and exits 2.
//!
//! ```rust
//! #[gamma::expect_killed(relational, reason = "the boundary here is load-bearing")]
//! fn within(value: u32, limit: u32) -> bool {
//!     value < limit
//! }
//! ```
//!
//! ```rust
//! #[gamma::expect_survived(reason = "logging only; nothing observes this")]
//! fn trace(&self) { ... }
//! ```
//!
//! An `expect_survived` mutant that is killed by a failing assertion is reported, and so is an
//! `expect_killed` mutant that survives, times out, exhausts memory, or is uncovered. Timeout and
//! memory exhaustion satisfy `expect_survived` because no assertion rejected the mutant. That makes
//! the annotation self-correcting: when somebody finally writes the test that covers this code, the
//! run tells you the note is stale instead of leaving it to rot.
//!
//! Use `expect_survived` where `skip` is tempting but the gap is a real gap you intend to close, and
//! `expect_killed` to pin coverage you consider load-bearing so that a later change cannot quietly
//! remove it.
//!
//! A mutant that never ran — one that failed to compile, or that was suppressed — is not judged, since
//! it is not evidence about the suite either way.
//!
//! To see what a mutator does and how to switch it off:
//!
//! ```bash
//! cargo gamma explain relational.lt_to_le   # a mutator
//! cargo gamma explain @arithmetic           # everything a preset selects
//! cargo gamma explain c6d3802ceceb           # a current mutant
//! cargo gamma explain --report old.json c6d3802ceceb # a mutant from a retained report
//! ```
//!
//! To write directives in bulk for the mutants a run could not decide on, see
//! [Fixing timeouts, out-of-memory and unviable mutants](#fixing-timeouts-out-of-memory-and-unviable-mutants).
//!
//! ## Configuration
//!
//! Command-line campaign settings can be persisted in `gamma.toml`, so a project that has settled
//! on a set of options does not have to repeat it in every CI job and every developer's shell
//! history. The file also holds project policy such as trait implementation exclusions:
//!
//! ```toml
//! mutators      = ["@arithmetic", "@relational", "stmt"]
//! exclude-files = ["src/generated/**"]
//! exclude-tests = ["conformance_*"]
//! min-score     = 70.0
//! artifact-dir  = "target/cargo-gamma"
//!
//! # Debug output is diagnostic text without a stable formatting contract.
//! exclude-trait-impls = ["Debug", "Display"]
//!
//! [shard]
//! count = 30
//! ```
//!
//! **[docs/CONFIG.md](docs/CONFIG.md) documents every key.** [docs/gamma.toml](docs/gamma.toml) is a
//! file listing all of them with their defaults, commented out — copy it and delete what you do not
//! need:
//!
//! ```bash
//! cp docs/gamma.toml gamma.toml
//! ```
//!
//! ### Where the file lives
//!
//! `gamma.toml`, in the directory being analyzed. Two flags override that:
//!
//! ```bash
//! cargo gamma run --config ci/gamma.toml   # read this file instead
//! cargo gamma run --no-config              # read nothing
//! ```
//!
//! An explicit `--config` path must exist. Asking for a file and silently getting the defaults because
//! the path was misspelled is exactly the failure that check is there to prevent, whereas a missing
//! conventional file is the ordinary case and is not an error.
//!
//! ### Precedence
//!
//! A flag given on the command line wins over the file; the file wins over the built-in default. For
//! the list-valued keys — `files`, `packages`, `errors` and the rest — the two **concatenate**, so
//! adding one exclusion at the command line does not silently drop the ones the project agreed on. Use
//! `--no-config` for a run that should ignore what is checked in entirely.
//!
//! ```toml
//! # gamma.toml
//! mutators = [
//!     "@all",
//!     "!stmt",   # too many equivalent mutants in the parser
//! ]
//! exclude-files = ["src/generated/**"]
//! min-score = 80.0
//! ```
//!
//! `.cargo/mutants.toml` is not supported and is never read. Configure Gamma independently in
//! `gamma.toml`.
//!
//! ## Known gaps
//!
//! Doctests are not built or run, so a mutant whose only coverage is a doctest is reported as a survivor.
//!
//! Types are read syntactically rather than resolved, so a type alias is not seen through and falls
//! back to `Default::default()`. Where the tool can tell that no such guess could hold — a bare type
//! parameter, an associated type projected out of one, a `Box<dyn Trait>`, or an `impl Trait` that is
//! not an iterator — it withholds the mutant instead of generating one that cannot compile. The same
//! absence of type resolution is why `expr` occasionally perturbs an expression that turns out not to
//! be numeric.
//!
//! An `impl Trait` return that is not an iterator is still passed over. `impl Future` is the case that
//! comes up most, and cargo-mutants does not cover it either.
//!
//! ## Reports
//!
//! ```bash
//! cargo gamma run                                             # JSON, HTML and SARIF reports
//! cargo gamma run --artifact-dir reports                      # move every artifact together
//! ```
//!
//! Every run writes all three reports without being asked. Normally they are
//! `target/cargo-gamma/gamma-report.html`, `gamma-report.json`, and `gamma-report.sarif` in the
//! original workspace. [`--artifact-dir`](#controlling-a-run) moves the complete set to another
//! directory and creates it when necessary. `--cache-dir` only relocates internal reusable state.
//!
//! The JSON is the [`mutation-testing-elements`](https://github.com/stryker-mutator/mutation-testing-elements)
//! interchange format, which is what the Azure DevOps and GitHub mutation report extensions consume, so
//! no translation step is needed.
//!
//! The HTML is a single self-contained file: the viewer and the results are both embedded, so it opens
//! from a CI artifact, a file share, or a machine with no network at all. There is no mode that loads
//! the viewer from elsewhere: loading an external viewer would allow remotely supplied JavaScript to
//! read and disclose the source and results embedded in the report.
//!
//! ## Improving mutation performance
//!
//! Mutation testing with schemas is orders of magnitude faster than rebuilding per mutant because the
//! build is paid once for the entire population. Even so, running thousands of mutants against a large
//! test suite takes time. Improving performance means knowing where time is spent and applying the
//! right combination of levers: checking in hints for warm CI runs,
//! incremental runs with `--incremental`, and tuning
//! timeouts and harness options.
//!
//! ### Where the time goes
//!
//! A run's cost separates into build, baseline, and per-mutant work; identifying the dominant term
//! guides performance tuning.
//!
//! ```text
//! total ≈ build × rounds  +  baseline  +  Σ per mutant (launch + tests until something fails)
//!         └── fixed ──────────────────┘   └── scales with the population ──────────────────┘
//! ```
//!
//! **The build is the fixed cost, and it buys the whole population.** Every mutant lives in one
//! compiled artifact, selected at runtime, so a build is paid once no matter how many mutants there
//! are. This inverts the usual advice: with a per-mutant build you minimize the population, and here
//! you can afford a much richer catalog. It also means a *narrow* run is the worst deal this tool
//! offers — asking for twenty mutants in one file still pays the whole build. If you are iterating on a
//! single file, expect the build to dominate completely, and leverage `--incremental` (enabled by
//! default) across runs so unchanged files are amortized.
//!
//! **A build is "once" but not always "one".** Mutants that cannot compile are found by building,
//! reading the compiler's diagnostics, withdrawing the mutants they blame, and building again.
//! Compilation is phased — a type error hides the borrow error behind it — so unviable mutants surface
//! in waves, and each wave is another round. A workspace where a quarter of the population does not
//! compile can spend a long time here before a single test runs.
//!
//! You mostly pay this once. A run remembers which mutants failed to compile and skips them next time,
//! so a second run over an unchanged tree spends materially fewer rounds:
//!
//! ```text
//!       Cached 3356 mutants known not to compile, carried forward rather than rebuilt
//! ```
//!
//! The memory is a record and nothing depends on it. It stops being *believed* whenever the file a
//! mutant lives in changes, whenever the features differ, and whenever the toolchain does — because a
//! mutant that could not compile against one version of the surrounding types may compile against the
//! next, and assuming otherwise would quietly drop a mutant that might have survived out of the
//! denominator. What survives a mismatch is the order: the mutants that failed last time are still the
//! ones worth compiling first, and being wrong about that costs the order and nothing else. Deleting
//! `last-gamma-run.json` only ever costs time, and `--incremental no` ignores it for one run.
//!
//! **Killed mutants are cheap; survivors are expensive.** A kill stops at the first test that fails —
//! the run stops at the first binary that convicts, and inside that binary at the first failing test.
//! A survivor has to run every reachable test binary to completion in order to prove that nothing
//! caught it. A codebase with many survivors is slow *because* it is under-tested, and the run gets
//! faster as the suite gets better.
//!
//! **A re-run goes straight to the test that caught each mutant.** "The first test that fails" still
//! means paying for every test the harness happened to run ahead of it. A run records which test caught
//! each mutant, and the next run tries that one test on its own before anything else — so a re-killed
//! mutant costs one test rather than a partial binary. On a suite where the killing test is halfway
//! down the list, this is most of the sweep.
//!
//! It is a guess, and it is checked rather than believed: the named test is actually run, and if it no
//! longer exists, no longer fails, or names a binary this run has excluded, the ordinary whole-binary
//! sweep runs behind it and reaches exactly the verdict it would have reached anyway. Nothing here can
//! move a score. A stale entry costs one filtered test process and is then dropped.
//!
//! The run record holds it, keyed by mutant identity, so an edit elsewhere in the file leaves it intact
//! and a site that genuinely changed simply stops matching. It sits outside the digested part of the
//! record on purpose: a feature change discards cached build facts, and discarding the hints along with them
//! would make the map cold on exactly the runs it is worth the most on. `--incremental no` turns it off
//! along with everything else the record holds.
//!
//! **Your suite's speed is the multiplier.** It is paid once per mutant, so a fixture built per test
//! process, a sleep, or a network call is not slow once — it is slow thousands of times. This is
//! usually the single highest-leverage thing to fix, and unlike everything else here it costs no
//! signal and helps every other workflow.
//!
//! **Hangs cost more than everything else.** A mutant that turns a loop counter into an infinite loop
//! is stopped only by its timeout, and then re-run to confirm the timeout was real. That budget has a
//! floor under it, so on a fast suite one hang can cost what several thousand judged mutants cost.
//! `--minimum-test-timeout` lowers the floor and makes every hang cheaper.
//!
//! **Instrumented code is not free.** Every mutation site becomes a runtime branch, and tests build
//! without optimizations by default, where those branches are real calls rather than something the
//! optimizer folds away. A crate whose tests drive a hot path through mutated code pays that on every
//! mutant run. If your suite is compute-heavy and your crate builds quickly, this is the case where an
//! optimized profile can recover the most time.
//!
//! ### Optimizing compute-heavy suites
//!
//! Use a custom profile when the diagnostics show that mutant execution, rather than builds or I/O,
//! dominates the run. Parser, regex, cryptography, schema, numeric, and transformation suites are
//! common candidates. Put this profile in the workspace root `Cargo.toml`:
//!
//! ```toml
//! [profile.gamma]
//! inherits = "dev"
//! opt-level = 2
//! debug-assertions = true
//! overflow-checks = true
//! ```
//!
//! Then select it explicitly:
//!
//! ```bash
//! cargo gamma run --profile gamma
//! ```
//!
//! The profile keeps development-mode assertions and overflow checks while optimizing the code paid
//! once per mutant. It remains opt-in because it is not a universal win: a build-heavy narrow run
//! may spend more compiling than it saves, and an I/O-bound suite has little CPU time to recover.
//! Compare a representative shard before adopting it for a long campaign.
//!
//! The active Cargo profile is part of a run's build context. Changing it invalidates ordinary
//! unviability reuse, but not the compiler-exclusion inventory. Changed code generation can change which mutants tests catch even
//! when development checks remain enabled. Scores produced under different profiles are therefore
//! not directly comparable. Choose the profile before a long campaign and keep it fixed across every
//! run or shard whose scores will be compared or merged.
//!
//! ### Checking in the hints file
//!
//! Every cold run — such as a fresh CI container where `target/` is not preserved — starts with an
//! empty killer map and an unguided build, on exactly the runs that cost the most.
//! `cargo gamma hints` promotes scheduling information that cannot move a score directly from the
//! latest persisted campaign into a file you can commit:
//!
//! ```bash
//! cargo gamma run                       # learn killer tests and unviable mutants
//! cargo gamma hints --dry-run           # see what would be promoted
//! cargo gamma hints                     # update gamma-hints.yaml
//! ```
//!
//! Later runs read `gamma-hints.yaml` automatically without any command-line flags. Inferred tests
//! are candidates proposed from related items, files, or observed reachability; an exact killer hint
//! identifies the previous killer for the same mutant. Three kinds of information are promoted into
//! the hints file, and none can change an answer:
//!
//! - **Killer hints** — which test caught each mutant. The named test is tried before inferred tests,
//!   regardless of its binary's position. A kill settles immediately without running
//!   earlier binaries. It is run rather than believed, so a stale hint falls
//!   back to inferred selection and the standard whole-binary sweep. Duplicate targeted attempts
//!   are skipped without removing resource retries, nextest enumeration controls, or necessary
//!   whole-binary fallback.
//! - **Test-selection hints** — ranked candidate tests for an item, candidate binaries for
//!   a source file, and test sets observed reaching a stable source site. Every candidate is run
//!   again as inferred tests and falls back conservatively when stale; no prior verdict is carried
//!   forward. The persisted section retains its `generalized` name for compatibility.
//! - **Build order** — which mutants failed to compile for whoever promoted the file. These are *not*
//!   carried as verdicts. They are spliced in and offered to the compiler first, on their own, so a
//!   mutant that really is unviable is blamed in a single probe round without another mutant's error
//!   hiding it. A hint that is wrong produces a mutant that compiles, stays live, and is judged exactly
//!   as if it had never been named.
//!
//! The version-4 YAML layout groups mutants by workspace-relative source file. Within each file,
//! repeated killer identities are stored once in a table and mutants refer to them by index, so a
//! large run does not repeat the same path, package, target, and test for every adjacent mutant.
//! Groups and tables are sorted deterministically for stable reviewable diffs. Ordinary promotion
//! merges the selected run's knowledge into the artifact and preserves everything outside that
//! selection; `--replace` intentionally rebuilds it from only the selected population. Its
//! `context` contains only the generating repository's full HEAD SHA and UTC date. These identify
//! the artifact generation rather than gating hints or attributing retained entries to that commit.
//! Incremental promotion refuses an existing artifact it cannot understand rather than replacing
//! unknown knowledge with a partial generation; `--replace` is the explicit permission to discard
//! it. This includes a newer independently versioned generalized section, whose future fields
//! cannot be round-tripped by today's serializer. YAML changed by another writer after loading is
//! left intact and reported as a conflict. Malformed, foreign, and unsupported artifacts remain
//! safe to ignore.
//!
//! ### Learning reachability while testing
//!
//! Every campaign drains exact killer hints first. Each worker then finishes one unresolved mutant
//! through inferred named tests, inferred full binaries, and remaining reachable full binaries
//! before claiming another. New killers supply hints for queued siblings. Full-binary results are
//! reused across the last two stages. Both passes honor `--jobs`, without an additional
//! CPU-count cap or aggregate memory reservation. Reduce `--jobs` if concurrent runs exhaust system
//! memory; per-process memory limits and named-resource constraints remain enforced.
//!
//! Remaining full binaries run own-package first, then by descending distinct-mutant kill count
//! within each package tier. Known exact hints seed the counts and observed kills update them
//! live without crediting the same mutant twice. Baseline runtime and stable identity break ties.
//!
//! Each ordinary mutant launch records whether its active guard was reached,
//! without a separate build or sampling subprocess. Positive observations improve later test ordering.
//!
//! A complete, sealed, passing whole-binary observation that did not reach the active guard may
//! exclude that binary for another replacement at the exact same stable source site. If no
//! reachable binary remains, gamma reports **uncovered** rather than a survivor. Filtered, failed,
//! missing, or incomplete observations never prove absence.
//!
//! This transfer assumes deterministic reachability. For suites whose control flow depends on
//! threads, the clock, randomness, or hash iteration order, `--whole-test-binaries` suppresses
//! case-level selection and deterministic reach narrowing:
//!
//! ```bash
//! cargo gamma run --whole-test-binaries
//! ```
//!
//! ### Incremental runs
//!
//! During development, a previous run can avoid repeated compiler work without carrying its test
//! verdicts into a new score:
//!
//! ```bash
//! cargo gamma run --incremental build         # default: reuses ordinary compiler outcomes and checked hints
//! cargo gamma run --incremental no            # no incremental reuse; compiler exclusions still apply
//! ```
//!
//! In `build` mode (the default), ordinary cached compiler outcomes require matching compilation
//! context and inputs. The separate compiler-exclusion inventory remains authoritative in both
//! modes, until its exact mutant entry is removed. Every other killed, survived, timed-out,
//! out-of-memory, flaky, ignored, or uncovered mutant is evaluated again. A previous killer is
//! tried first, but its result is checked and the ordinary suite follows when it no longer kills
//! the mutant. See [Compiler exclusions](docs/CMDLINE.md#compiler-exclusions) before retrying a
//! repaired identity.
//!
//! ### Resuming interrupted testing
//!
//! Once mutant testing starts, each settled verdict is persisted before its progress is reported.
//! After Ctrl+C or a host restart, continue that same campaign with:
//!
//! ```bash
//! cargo gamma run --resume-testing
//! ```
//!
//! Resume reuses the copied, instrumented workspace and compiled tests without rebuilding or
//! repeating settled mutants. Only unfinished work runs again. The original selection and testing
//! settings stay frozen, even if the live source, hints, or suppressions change. Missing, damaged,
//! incompatible, completed, or pre-testing state is an error, not a request to start over.
//! Campaigns started before this feature do not contain the required checkpoint.
//!
//! `cargo gamma hints`, `cargo gamma suppress`, and `cargo gamma unsuppress` also work after an
//! interruption. Hints and suppressions use recorded partial evidence; pending mutants are not
//! treated as verdicts. Use the same `--cache-dir` when a campaign used a redirected cache.
//!
//! ### What a run remembers
//!
//! Three things carry knowledge from one run to the next, and they differ in exactly one way that
//! matters: **how long they last, and therefore how much they are believed.**
//!
//! ```mermaid
//! flowchart TD
//!     subgraph Run1 [Run N: Execution]
//!         A[Run test suite] --> B[Discover killer tests &<br/>unviable mutants]
//!         B --> C[(last-gamma-run.json<br/><i>Local run cache</i>)]
//!     end
//!
//!     subgraph Hints [Version Control]
//!         C -->|cargo gamma hints| D[(gamma-hints.yaml<br/><i>Checked-in hints</i>)]
//!     end
//!
//!     subgraph Run2 [Run N+1 / Fresh CI: Accelerated Sweep]
//!         D --> E[Run known killer test first]
//!         D --> F[Front-load unviable mutants]
//!         C -->|--incremental build| G[Reuse matching compiler unviability]
//!     end
//! ```
//!
//! | | Lives in | Lasts | Believed |
//! |---|---|---|---|
//! | The run record | cache `last-gamma-run.json` | Until the cache is deleted | Matching compiler unviability; killer tests are checked hints |
//! | The hints file | `gamma-hints.yaml` | Forever, and through review | Never — every hint is checked |
//! | A skip directive | Your source | Forever, and through review | Always — you wrote it |
//!
//! The completed record is the tool's durable memory and is published only after a measured
//! campaign finishes. It holds the complete outcome ledger and, for incremental campaigns,
//! compiler unviability and checked killer hints alongside a cryptographic snapshot of compilation
//! inputs. A failed incremental campaign writes only best-effort scheduling knowledge to
//! `incomplete-gamma-learning.json`; it never replaces the completed ledger. Dry runs write no
//! campaign state, and `--incremental no` neither reads nor writes incomplete learning. Records are
//! keyed by the mutant's content id, which hashes the file, the item path, the mutator and the
//! replacement.
//!
//! A run adopts only compiler unviability under a matching build context. Test outcomes are
//! observations that can vary even when every captured input is unchanged, so a new campaign never
//! reuses them. Testing-only resume instead continues the same frozen campaign.
//!
//! **A skip directive is not a cache and must never be treated as one.**
//! `cargo gamma suppress --apply` writes a suppression directive into the source, where it is
//! reviewed, committed, and survives a clean checkout. That is a claim you stand behind.
//! Everything in the record — and everything in the hints file, committed or
//! not — is a convenience that must be safe to delete: removing the cargo-gamma cache and
//! `rm gamma-hints.yaml` cost you time and nothing else.
//!
//! ### Diagnosing a slow run
//!
//! Mutation testing is the kind of tool that gets adopted enthusiastically, runs for four hours, and is
//! then quietly deleted from the CI configuration. Cargo-gamma writes an advice document on every
//! run so that measured bottlenecks can be addressed instead of guessed at:
//!
//! ```bash
//! cargo gamma run --artifact-dir reports # write every user-facing artifact here
//! ```
//!
//! The advice document is written on every run, to `target/cargo-gamma/gamma-perf-advice.md`;
//! `--artifact-dir` moves it with the other artifacts. It turns a finished run into a Markdown document: a list of findings, each a measured
//! symptom, a named cause, a remedy, and — never omitted — what the remedy costs in signal. It closes
//! with the per-family cost and survivor table the low-yield finding is drawn from.
//!
//! ```markdown
//! ### crates/parser/src/tables.rs alone is 34% of the population (1 204 mutants)
//!
//! - 11m of CPU time, 2 survivors found there
//!
//! **Remedy.** If it is generated, tabular or macro-expanded code, exclude it with `--exclude-file` or
//! the `exclude-files` config key. If it is hand-written, this is not a problem — it is where the logic
//! is.
//!
//! **Costs.** Exactly 1204 mutants stop being tested, 2 of which are currently finding gaps in the
//! suite.
//!
//! ### Yield by family
//!
//! | Family | Mutants | CPU | Survivors | Survivors/CPU-h |
//! |---|---:|---:|---:|---:|
//! | `relational` | 4 210 | 22m | 61 | 166.4 |
//! | `arith` | 1 980 | 11m | 9 | 49.1 |
//! ```
//!
//! Every mitigation available here trades information for time. A recommendation that hides the trade
//! is worse than no recommendation, because it will be taken.
//!
//! The same diagnosis is appended to the GitHub Actions job summary whenever `--annotations` is
//! active, so the panel a team reads every morning carries not just the score but what to do about it.
//!
//! #### Reporting a run that went wrong
//!
//! Advice is written for the person who owns the tree. A bug report is read by someone who does not
//! have it, cannot run it, and has only what the reporter thought to paste. So every run also writes
//! `target/cargo-gamma/gamma-diagnostics.json`: one machine-readable record of what the run did, small enough to
//! attach to an issue.
//!
//! ```bash
//! cargo gamma run --artifact-dir reports      # move the bundle with the other artifacts
//! cargo gamma run --diag-names names          # do not redact; the tree is public anyway
//! ```
//!
//! It is written unconditionally, and on purpose. A flag that has to be set *before* the run is one
//! nobody has set by the time the run turns out to be slow, or wrong, or six hours long — and asking
//! for it again means paying for the whole run twice.
//!
//! The document holds the tool and toolchain versions, the host's shape, the settings that actually
//! took effect and where each came from, the population and its outcomes, per-round build results with
//! rustc error codes, the withdrawal reasons, the wall-clock split between fixed cost and testing, the
//! duration percentiles, and the per-mutator and per-package breakdowns. It never holds source text,
//! never holds a mutant's replacement expression, and never holds an absolute path — a test asserts all
//! three.
//!
//! Names are hashed by default: package, file and binary names become twelve hex characters, which
//! keeps rows distinguishable and groupable without naming an unreleased codebase. `--diag-names names`
//! turns that off and `--diag-names omitted` drops the names entirely. Mutator names and rustc error
//! codes are never redacted, because they are the tool's own vocabulary and the mutator axis is the
//! most useful thing in the document.
//!
//! The file carries its own `schemaVersion`, independent of the mutation-testing-elements version the
//! JSON report follows, so a diagnostic tool can tell what it is reading.
//! Its aggregate telemetry is intended to answer whether the algorithms paid off without making the
//! bundle scale one row per mutant. Build rounds show package-attributed withdrawals against the real
//! workspace-wide round duration. Sweep fields show whole-binary and uncovered decisions,
//! candidate/probe/hit funnels by hint tier, launches saved, and overlapping package wall spans.
//! Package CPU totals and wall spans are deliberately separate: concurrent packages can occupy the same
//! wall-clock interval, and Cargo does not expose a truthful per-package share of a workspace build round.
//!
//! ### What to reach for
//!
//! What to reach for, in rough order of what it costs you:
//!
//! | If the time is going to | Try | What it costs |
//! |---|---|---|
//! | the suite itself | make the suite faster; check for per-test fixtures, sleeps and network calls | nothing |
//! | cold CI runs starting from scratch | check in `gamma-hints.yaml` with `cargo gamma hints` | nothing |
//! | the build, on a narrow run | incremental execution (`--incremental`) | nothing |
//! | non-deterministic test reachability | `--whole-test-binaries` | every selected case in a reachable binary is repeated for every mutant |
//! | unviable convergence across checkouts | `cargo gamma hints` or `cargo gamma suppress --apply` | nothing |
//! | hangs | lower `--minimum-test-timeout` | a genuine hang is caught sooner but a slow test may be misread as one |
//! | a whole-population run in CI | `--shard-count` with `cargo gamma merge` | a verdict is up to one rotation old |
//! | one generated or tabular file | `--exclude-file` | those mutants stop being tested |
//! | the population being too broad | narrow `--mutators` | whole mutator families stop being tested |
//!
//! The last three trade information for time. Everything above them does not.
//!
//! ## Continuous integration
//!
//! A mutation report that lives in an artifact zip is a report nobody reads, so the findings are
//! delivered where the reviewer already is.
//!
//! ```bash
//! cargo gamma run --artifact-dir reports        # then upload reports/gamma-report.sarif
//! ```
//!
//! Inside GitHub Actions, no flag is needed at all: `--annotations` defaults to `auto`, which detects
//! the runner and then writes surviving mutants to the diff as workflow annotations and a score table
//! to the job summary. `--annotations none` turns it off, `--annotations github` forces it on.
//!
//! All three surfaces publish **survivors only**. A killed mutant is the tool working, and reporting it
//! would bury the signal under its own success. Uncovered mutants are included, because "no test
//! reaches this" is a stronger finding than "a test reached it and said nothing".
//!
//! Both surfaces are capped at what GitHub actually accepts: ten annotations per step, because that is
//! all GitHub keeps of a level and printing more produces a log full of commands that had no effect,
//! and five thousand SARIF results within ten megabytes, because a larger upload is rejected whole
//! rather than trimmed. When a cap bites, the run says so and the full population stays in the report.
//!
//! SARIF rule identifiers are the stable mutator names, so GitHub's grouping and dismissal work per
//! mutator: a team can permanently dismiss every `literal.int_zero` alert without touching anything
//! else, and that decision keeps applying to code written next year. Results are fingerprinted by the
//! content-addressed mutant ID, so an alert follows its code through reformatting instead of being
//! dismissed and resurrected. The level is `note` by default, because a surviving mutant is an
//! observation about the test suite rather than a defect in the code, and drowning the security tab is
//! how a good signal gets turned off; `--sarif-level warning` raises it.
//!
//! ### Exit codes
//!
//! | Code | Meaning |
//! | ---- | ------- |
//! | `0` | The run finished and every gate that was configured passed. |
//! | `1` | Usage error: an argument, a filter pattern or a configuration key was wrong. |
//! | `2` | The run finished and a gate failed: score was below `--min-score`, or a `gamma::expect` was contradicted. |
//! | `3` | The run could not proceed: the baseline failed, the tree would not compile, or scratch dir was in use. |
//! | `70` | An internal error. This is a bug; the message says what to report. |
//!
//! **Surviving mutants do not fail the process on their own.** A run with survivors and no gate exits
//! `0`, deliberately: adopting mutation testing on an existing codebase starts with survivors, and a
//! tool that fails the build on the first day is a tool that gets removed on the second. A CI job that
//! wants survivors to be fatal says so:
//!
//! ```bash
//! cargo gamma run --min-score 100          # any survivor fails the job
//! cargo gamma run --min-score 80           # a ratchet you can raise over time
//! ```
//!
//! `--min-score` is the ratchet worth reaching for. Setting it to the score you have today makes the
//! number impossible to lose ground on, and raising it is a one-line change with a visible owner.
//!
//! ### Example GitHub Actions workflow
//!
//! To run mutation testing on pull requests and track results in GitHub:
//!
//! ```yaml
//! name: Mutation Testing
//!
//! on:
//!   pull_request:
//!     branches: [main]
//!
//! jobs:
//!   gamma:
//!     runs-on: ubuntu-latest
//!     steps:
//!       - uses: actions/checkout@v4
//!         with:
//!           fetch-depth: 0
//!
//!       - uses: dtolnay/rust-toolchain@stable
//!
//!       - uses: Swatinem/rust-cache@v2
//!
//!       - name: Install cargo-gamma
//!         run: cargo install cargo-gamma
//!
//!       - name: Run mutation testing on changed files
//!         run: |
//!           git diff origin/main | cargo gamma run \
//!             --in-diff - \
//!             --min-score 80 \
//!             --artifact-dir target/cargo-gamma
//!
//!       - name: Upload SARIF report
//!         uses: github/codeql-action/upload-sarif@v3
//!         if: always()
//!         with:
//!           sarif_file: target/cargo-gamma/gamma-report.sarif
//!           category: cargo-gamma
//!
//!       - name: Upload HTML report
//!         uses: actions/upload-artifact@v4
//!         if: always()
//!         with:
//!           name: mutation-report
//!           path: target/cargo-gamma/gamma-report.html
//! ```
//!
//! ### Sharding
//!
//! Mutation testing a large workspace exhaustively does not fit in a nightly CI budget. Split the
//! population into shards and run a different one each night:
//!
//! ```bash
//! cargo gamma run --shard-count 30 --shard-index 7
//! ```
//!
//! Shards are assigned by hashing each mutant's content-addressed identity, and two consequences matter
//! in practice. A mutant keeps its shard as the code around it changes, so coverage accumulates across
//! nights instead of resetting whenever somebody edits a file. And raising the shard count moves only
//! the mutants that have to move, rather than reshuffling everything and throwing away the rotation you
//! had already paid for.
//!
//! Keep each night's report and merge the rotation to get a score for the whole population:
//!
//! ```bash
//! cargo gamma run --shard-count 30 --shard-index $((10#$(date +%j) % 30)) \
//!     --artifact-dir reports/$(date +%F)
//! cargo gamma merge reports --window 45 --min-score 70 --html-report merged.html
//! ```
//!
//! Merging unions verdicts by mutant identity and keeps the most recent one. Because identity is
//! content-addressed, a mutant whose code has since been edited is not credited with the verdict its
//! predecessor earned — it reappears as never tested, which is also how it stays out of the
//! denominator.
//!
//! Removing the *old* identity needs one more thing, because a union by itself never drops anything: at
//! least one input has to be an unsharded, unfiltered run or listing, which states the complete
//! population of every file it covers. An identity absent from the newest complete input has been
//! withdrawn, and the summary counts it under `Withdrawn`.
//!
//! A report narrowed by `--re` is partial even without sharding, so it cannot establish a withdrawal.
//! If its source text changed, an available unfiltered source stays the presentation for unmatched
//! historical mutants; include a current unfiltered listing for current presentations. A sharded
//! report describes only its own slice, so it never withdraws anything — merge a full
//! `list mutants --json` alongside the rotation to keep the denominator honest:
//!
//! ```bash
//! cargo gamma list mutants --json-report reports/current.json
//! cargo gamma merge reports --window 45
//! ```
//!
//! The summary also reports how fresh the verdicts are and which shards the rotation has yet to visit.

#![forbid(
    unsafe_code,
    reason = "every raw platform call in this workspace lives in `cargo-gamma-unsafe`, behind a safe interface; enforcing that here is what makes it a property the compiler checks rather than a convention people remember"
)]
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]

mod real_host;

use std::{env, process};

use cargo_gamma_lib::run;
use real_host::RealHost;

// `rallocator` supports only Linux and Windows hosts; others use the system allocator.
#[cfg(all(not(miri), any(target_os = "linux", windows)))]
rallocator::rallocator!();

#[cfg_attr(coverage_nightly, coverage(off))]
fn main() -> process::ExitCode {
    if let Some(code) = cargo_gamma_lib::run_rustc_wrapper_if_requested(env::args_os()) {
        return code;
    }

    // `run` returns the process exit code rather than exiting itself, so that every code path
    // through the CLI is reachable from an ordinary integration test.
    u8::try_from(run(&mut RealHost, env::args_os())).map_or(process::ExitCode::FAILURE, process::ExitCode::from)
}