zshrs 0.11.5

The first compiled Unix shell — bytecode VM, worker pool, AOP intercept, Rkyv caching
Documentation
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
//! `compctl.h` port — completion descriptor types + `CC_*` / `CCT_*`
//! flag constants used by the legacy `compctl` builtin.
//!
//! Port of `Src/Zle/compctl.h`. Canonical home for the four typedefs
//! (`Compctlp`/`Compctl`/`Compcond`/`Patcomp`) and the two flag-bit
//! families: 30 `CC_*` primary completion-target flags (mask) and 7
//! `CC_*` secondary flags (mask2), plus 14 `CCT_*` `-x` condition
//! types.
//!
//! C source: 4 typedefs + 4 structs (`compctlp`, `patcomp`,
//! `compcond`, `compctl`), 14 `CCT_*` constants (c:76-89), 30
//! primary `CC_*` constants (c:118-149), 7 secondary `CC_*` constants
//! (c:152-158). 0 functions.
//!
//! `compctl.rs` (the .c port) re-exports these via `pub use
//! super::compctl_h::*;` so existing `cc_flags::FILES` / `cct::POS`
//! call sites keep compiling alongside the C-canonical
//! `CC_FILES` / `CCT_POS` names.

// ---------------------------------------------------------------------------
// `-x` condition type constants (c:76-89).
// ---------------------------------------------------------------------------


// --- AUTO: cross-zle hoisted-fn use glob ---
#[allow(unused_imports)]
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_utils::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;

pub const CCT_UNUSED:   i32 = 0;                                         // c:76
pub const CCT_POS:      i32 = 1;                                         // c:77
pub const CCT_CURSTR:   i32 = 2;                                         // c:78
pub const CCT_CURPAT:   i32 = 3;                                         // c:79
pub const CCT_WORDSTR:  i32 = 4;                                         // c:80
pub const CCT_WORDPAT:  i32 = 5;                                         // c:81
pub const CCT_CURSUF:   i32 = 6;                                         // c:82
pub const CCT_CURPRE:   i32 = 7;                                         // c:83
pub const CCT_CURSUB:   i32 = 8;                                         // c:84
pub const CCT_CURSUBC:  i32 = 9;                                         // c:85
pub const CCT_NUMWORDS: i32 = 10;                                        // c:86
pub const CCT_RANGESTR: i32 = 11;                                        // c:87
pub const CCT_RANGEPAT: i32 = 12;                                        // c:88
pub const CCT_QUOTE:    i32 = 13;                                        // c:89

// ---------------------------------------------------------------------------
// Primary completion-target flags (`mask`, c:118-149).
// Each bit selects one completion-source kind (files, vars, jobs, ...)
// the compctl spec expands.
// ---------------------------------------------------------------------------

pub const CC_FILES:      u64 = 1 <<  0;                                  // c:118
pub const CC_COMMPATH:   u64 = 1 <<  1;                                  // c:119
pub const CC_REMOVE:     u64 = 1 <<  2;                                  // c:120
pub const CC_OPTIONS:    u64 = 1 <<  3;                                  // c:121
pub const CC_VARS:       u64 = 1 <<  4;                                  // c:122
pub const CC_BINDINGS:   u64 = 1 <<  5;                                  // c:123
pub const CC_ARRAYS:     u64 = 1 <<  6;                                  // c:124
pub const CC_INTVARS:    u64 = 1 <<  7;                                  // c:125
pub const CC_SHFUNCS:    u64 = 1 <<  8;                                  // c:126
pub const CC_PARAMS:     u64 = 1 <<  9;                                  // c:127
pub const CC_ENVVARS:    u64 = 1 << 10;                                  // c:128
pub const CC_JOBS:       u64 = 1 << 11;                                  // c:129
pub const CC_RUNNING:    u64 = 1 << 12;                                  // c:130
pub const CC_STOPPED:    u64 = 1 << 13;                                  // c:131
pub const CC_BUILTINS:   u64 = 1 << 14;                                  // c:132
pub const CC_ALREG:      u64 = 1 << 15;                                  // c:133
pub const CC_ALGLOB:     u64 = 1 << 16;                                  // c:134
pub const CC_USERS:      u64 = 1 << 17;                                  // c:135
pub const CC_DISCMDS:    u64 = 1 << 18;                                  // c:136
pub const CC_EXCMDS:     u64 = 1 << 19;                                  // c:137
pub const CC_SCALARS:    u64 = 1 << 20;                                  // c:138
pub const CC_READONLYS:  u64 = 1 << 21;                                  // c:139
pub const CC_SPECIALS:   u64 = 1 << 22;                                  // c:140
pub const CC_DELETE:     u64 = 1 << 23;                                  // c:141
pub const CC_NAMED:      u64 = 1 << 24;                                  // c:142
pub const CC_QUOTEFLAG:  u64 = 1 << 25;                                  // c:143
pub const CC_EXTCMDS:    u64 = 1 << 26;                                  // c:144
pub const CC_RESWDS:     u64 = 1 << 27;                                  // c:145
pub const CC_DIRS:       u64 = 1 << 28;                                  // c:146
pub const CC_EXPANDEXPL: u64 = 1 << 30;                                  // c:148
pub const CC_RESERVED:   u64 = 1 << 31;                                  // c:149

// ---------------------------------------------------------------------------
// Secondary completion-target flags (`mask2`, c:152-158).
// ---------------------------------------------------------------------------

pub const CC_NOSORT:  u64 = 1 << 0;                                      // c:152
pub const CC_XORCONT: u64 = 1 << 1;                                      // c:153
pub const CC_CCCONT:  u64 = 1 << 2;                                      // c:154
pub const CC_PATCONT: u64 = 1 << 3;                                      // c:155
pub const CC_DEFCONT: u64 = 1 << 4;                                      // c:156
pub const CC_UNIQCON: u64 = 1 << 5;                                      // c:157
pub const CC_UNIQALL: u64 = 1 << 6;                                      // c:158

// ---------------------------------------------------------------------------
// Typedef structs (c:32-115).
//
// C uses linked lists threaded through `next` pointers. The Rust
// port substitutes `Option<Box<...>>` for the same self-referential
// chain; the linked-list semantics are preserved.
// ---------------------------------------------------------------------------

/// Port of `struct compctlp` from `Src/Zle/compctl.h:39-42`. Hash
/// table node entry holding a pointer to the compctl descriptor.
///
/// C definition (c:39-42):
/// ```c
/// struct compctlp {
///     struct hashnode node;
///     Compctl cc;
/// };
/// ```
///
/// The Rust port omits the `hashnode` head (zshrs's hashtable
/// machinery threads name+next through a separate scaffold) and
/// keeps the semantic payload — a pointer to the compctl descriptor.
#[derive(Debug, Clone)]
#[allow(non_camel_case_types)]
pub struct Compctlp {                                                    // c:39
    pub cc: std::sync::Arc<Compctl>,                                                // c:41
}

/// Port of `struct patcomp` from `Src/Zle/compctl.h:46-50`. Linked-
/// list node for the pattern-compctl registry (entries created by
/// `compctl -p PATTERN ...`).
///
/// C definition (c:46-50):
/// ```c
/// struct patcomp {
///     Patcomp next;
///     char *pat;
///     Compctl cc;
/// };
/// ```
#[derive(Debug, Clone)]
#[allow(non_camel_case_types)]
pub struct Patcomp {                                                     // c:46
    pub next: Option<Box<Patcomp>>,                                      // c:47
    pub pat: String,                                                     // c:48
    pub cc: std::sync::Arc<Compctl>,                                                // c:49
}

/// Port of `struct compcond` from `Src/Zle/compctl.h:54-74`. The
/// per-condition descriptor for `compctl -x`.
///
/// C definition (c:54-74):
/// ```c
/// struct compcond {
///     Compcond and, or;
///     int type;            /* one of CCT_* */
///     int n;               /* array length */
///     union {
///         struct { int *a, *b; } r;       /* CCT_POS, CCT_NUMWORDS */
///         struct { int *p; char **s; } s;  /* CCT_CURSTR, CCT_CURPAT, ... */
///         struct { char **a, **b; } l;     /* CCT_RANGESTR, ... */
///     } u;
/// };
/// ```
///
/// The Rust port collapses C's `union` into an explicit enum
/// (`CompcondData`) since Rust unions require unsafe; the dispatch
/// is by `typ` per the C convention.
#[derive(Debug, Clone, Default)]
#[allow(non_camel_case_types)]
pub struct Compcond {                                                    // c:54
    pub and: Option<Box<Compcond>>,                                      // c:55
    pub or:  Option<Box<Compcond>>,                                      // c:55
    pub typ: i32,                                                        // c:56  (Rust keyword `type`)
    pub n:   i32,                                                        // c:57
    pub u:   CompcondData,                                               // c:58 union
}

/// Port of the anonymous `union { struct r,s,l }` inside `compcond`
/// at `Src/Zle/compctl.h:58-73`. The C union is dispatched by
/// `typ` (one of the `CCT_*` constants).
#[derive(Debug, Clone, Default)]
#[allow(non_camel_case_types)]
pub enum CompcondData {                                                  // c:58
    /// Port of `struct { int *a, *b; } r` (c:59-62) — used by
    /// `CCT_POS`, `CCT_NUMWORDS`.
    R { a: Vec<i32>, b: Vec<i32> },
    /// Port of `struct { int *p; char **s; } s` (c:63-66) — used by
    /// `CCT_CURSTR`, `CCT_CURPAT`, `CCT_CURSUF`, `CCT_CURPRE`,
    /// `CCT_CURSUB`, `CCT_CURSUBC`, `CCT_WORDSTR`, `CCT_WORDPAT`,
    /// `CCT_QUOTE`.
    S { p: Vec<i32>, s: Vec<String> },
    /// Port of `struct { char **a, **b; } l` (c:68-71) — used by
    /// `CCT_RANGESTR`, `CCT_RANGEPAT`.
    L { a: Vec<String>, b: Vec<String> },
    /// Empty (CCT_UNUSED).
    #[default]
    Unused,
}

/// Port of `struct compctl` from `Src/Zle/compctl.h:93-115`. The
/// real per-command compctl descriptor — what `compctl name args`
/// allocates and registers in the `compctltab` hashtable.
///
/// C definition (c:93-115) — 22 fields. Field names + types
/// preserved verbatim; pointer types collapse to `Option<String>` /
/// `Option<std::sync::Arc<Compctl>>` etc. as appropriate.
#[derive(Debug, Clone, Default)]
#[allow(non_camel_case_types)]
pub struct Compctl {                                                     // c:93
    /// Reference count.
    pub refc: i32,                                                       // c:94
    /// Next compctl in a `-x` chain.
    pub next: Option<std::sync::Arc<Compctl>>,                                      // c:95
    /// Mask of completion-target flags (`CC_*`).
    pub mask: u64,                                                       // c:96
    /// Secondary mask of completion-target flags (`CC_*`, mask2).
    pub mask2: u64,                                                      // c:96
    /// `-k` variable name.
    pub keyvar: Option<String>,                                          // c:97
    /// `-g` glob pattern.
    pub glob: Option<String>,                                            // c:98
    /// `-s` expansion string.
    pub str: Option<String>,                                            // c:99 (Rust keyword `str`)
    /// `-K` function name.
    pub func: Option<String>,                                            // c:100
    /// `-X` explanation.
    pub explain: Option<String>,                                         // c:101
    /// `-y` user-defined description for listing.
    pub ylist: Option<String>,                                           // c:102
    /// `-P` prefix.
    pub prefix: Option<String>,                                          // c:103
    /// `-S` suffix.
    pub suffix: Option<String>,                                          // c:103
    /// `-l` command name to use.
    pub subcmd: Option<String>,                                          // c:104
    /// `-1` command name to use.
    pub substr: Option<String>,                                          // c:105
    /// `-w` with-directory.
    pub withd: Option<String>,                                           // c:106
    /// `-H` history pattern.
    pub hpat: Option<String>,                                            // c:107
    /// `-H` number of events to search.
    pub hnum: i32,                                                       // c:108
    /// `-J`/`-V` group name.
    pub gname: Option<String>,                                           // c:109
    /// `-x` first compctl in the chain.
    pub ext: Option<std::sync::Arc<Compctl>>,                                       // c:110
    /// `-x` condition for this compctl.
    pub cond: Option<Box<Compcond>>,                                     // c:111
    /// `+` xor'ed compctl chain.
    pub xor: Option<std::sync::Arc<Compctl>>,                                       // c:112
    /// `-M` matcher control — head of the Cmatcher chain compiled
    /// from this compctl's match-spec arg.
    pub matcher: Option<Box<crate::ported::zle::comp_h::Cmatcher>>,      // c:113
    /// `-M` matcher string.
    pub mstr: Option<String>,                                            // c:114
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Verifies CCT_* values per c:76-89.
    #[test]
    fn cct_constants_correct() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        assert_eq!(CCT_UNUSED, 0);
        assert_eq!(CCT_POS, 1);
        assert_eq!(CCT_CURSTR, 2);
        assert_eq!(CCT_QUOTE, 13);
    }

    /// Verifies CC_* primary mask values per c:118-149 — single-bit,
    /// non-overlapping.
    #[test]
    fn cc_primary_mask_bits_distinct() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let all = CC_FILES | CC_COMMPATH | CC_REMOVE | CC_OPTIONS
                | CC_VARS | CC_BINDINGS | CC_ARRAYS | CC_INTVARS
                | CC_SHFUNCS | CC_PARAMS | CC_ENVVARS | CC_JOBS
                | CC_RUNNING | CC_STOPPED | CC_BUILTINS | CC_ALREG
                | CC_ALGLOB | CC_USERS | CC_DISCMDS | CC_EXCMDS
                | CC_SCALARS | CC_READONLYS | CC_SPECIALS | CC_DELETE
                | CC_NAMED | CC_QUOTEFLAG | CC_EXTCMDS | CC_RESWDS
                | CC_DIRS | CC_EXPANDEXPL | CC_RESERVED;
        assert_eq!(all.count_ones(), 31);  // 30 sequential + 30 + 31 (skips bit 29)
    }

    /// Verifies the secondary mask values per c:152-158.
    #[test]
    fn cc_secondary_mask_values() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        assert_eq!(CC_NOSORT, 1);
        assert_eq!(CC_XORCONT, 2);
        assert_eq!(CC_UNIQALL, 1 << 6);
    }

    /// Verifies Compctl Default initialiser zeroes every field per
    /// the C convention of `(Compctl) calloc(1, sizeof(...))`.
    #[test]
    fn compctl_default_zeros_fields() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let cc = Compctl::default();
        assert_eq!(cc.refc, 0);
        assert!(cc.next.is_none());
        assert_eq!(cc.mask, 0);
        assert_eq!(cc.mask2, 0);
        assert!(cc.keyvar.is_none());
        assert!(cc.cond.is_none());
        assert!(cc.xor.is_none());
        assert_eq!(cc.hnum, 0);
    }

    /// Verifies Compcond Default starts in CCT_UNUSED state.
    #[test]
    fn compcond_default_is_unused() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let c = Compcond::default();
        assert_eq!(c.typ, CCT_UNUSED);
        assert!(matches!(c.u, CompcondData::Unused));
    }

    /// Verifies the CompcondData variants align with the C union
    /// dispatch per c:58-73.
    #[test]
    fn compcond_data_variants() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let r = CompcondData::R { a: vec![0, 1], b: vec![2, 3] };
        if let CompcondData::R { a, b } = r {
            assert_eq!(a, vec![0, 1]);
            assert_eq!(b, vec![2, 3]);
        } else {
            panic!("expected R variant");
        }
        let s = CompcondData::S { p: vec![1], s: vec!["x".into()] };
        assert!(matches!(s, CompcondData::S { .. }));
        let l = CompcondData::L { a: vec!["lo".into()], b: vec!["hi".into()] };
        assert!(matches!(l, CompcondData::L { .. }));
    }

    /// c:76-89 — Every CCT_* constant is a unique non-negative
    /// integer. Pin uniqueness so a copy-paste regen doesn't double
    /// up a tag (which would silently route two different
    /// completion-condition kinds through the same dispatch arm).
    #[test]
    fn cct_constants_are_unique() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let all = [
            CCT_UNUSED, CCT_POS, CCT_CURSTR, CCT_CURPAT,
            CCT_WORDSTR, CCT_WORDPAT, CCT_CURSUF, CCT_CURPRE,
            CCT_CURSUB, CCT_CURSUBC, CCT_NUMWORDS,
            CCT_RANGESTR, CCT_RANGEPAT, CCT_QUOTE,
        ];
        let unique: std::collections::HashSet<_> = all.iter().copied().collect();
        assert_eq!(unique.len(), all.len(),
            "duplicate CCT_* constant detected");
        for &v in &all {
            assert!(v >= 0, "CCT_* constants must be non-negative");
        }
    }

    /// c:76 — CCT_UNUSED MUST be 0. The C source uses zero-init as
    /// the implicit "no condition" sentinel; a regression that sets
    /// CCT_UNUSED = 1 would mark every just-allocated condition as
    /// CCT_POS by accident.
    #[test]
    fn cct_unused_is_zero() {
        assert_eq!(CCT_UNUSED, 0, "CCT_UNUSED must be the zero-init sentinel");
    }

    /// c:118-127 — CC_* primary-mask bits are distinct singletons.
    /// Pin the bit-packing because the c:152 "primary mask" vs
    /// c:154-158 "secondary mask" distinction relies on the primary
    /// bits all being non-overlapping.
    #[test]
    fn cc_primary_mask_bits_are_distinct_singletons() {
        let primary = [
            CC_FILES, CC_COMMPATH, CC_REMOVE, CC_OPTIONS,
            CC_VARS, CC_BINDINGS, CC_ARRAYS, CC_INTVARS,
            CC_SHFUNCS, CC_PARAMS, CC_ENVVARS,
        ];
        for &m in &primary {
            assert_eq!(m.count_ones(), 1, "primary CC_ mask {} has {} bits set",
                m, m.count_ones());
        }
        let mut all: u64 = 0;
        for &m in &primary {
            assert_eq!(all & m, 0, "primary CC_ mask {} overlaps", m);
            all |= m;
        }
    }

    /// c:108 — `Compctl::default()` Default impl produces an empty
    /// struct ready for population. After a default + manual field
    /// write, the other fields must remain at their zero-init values.
    #[test]
    fn compctl_default_partial_population_doesnt_clobber_other_fields() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let mut cc = Compctl::default();
        cc.mask = CC_FILES;
        assert_eq!(cc.mask, CC_FILES);
        // Every OTHER field must still be at default
        assert_eq!(cc.refc, 0);
        assert!(cc.next.is_none());
        assert_eq!(cc.mask2, 0);
        assert!(cc.keyvar.is_none());
        assert!(cc.cond.is_none());
        assert_eq!(cc.hnum, 0);
    }

    /// `Compcond::default()` produces a CCT_UNUSED node with the
    /// `Unused` data variant. Pin the simultaneous shape so a
    /// regression that picks one but not the other (e.g. CCT_POS
    /// with Unused data) gets caught.
    #[test]
    fn compcond_default_typ_and_data_are_consistent() {
        let _g = crate::ported::zle::zle_main::zle_test_setup();
        let c = Compcond::default();
        assert_eq!(c.typ, CCT_UNUSED, "tag must be UNUSED");
        assert!(matches!(c.u, CompcondData::Unused),
            "data must be CompcondData::Unused");
    }

    /// c:118-149 — Full sweep of CC_* primary-mask bits 0..31. Each
    /// must occupy a distinct bit position. Catches a regen that
    /// renumbers two flags to the same shift.
    #[test]
    fn cc_primary_mask_full_sweep_no_overlap() {
        let primary = [
            CC_FILES, CC_COMMPATH, CC_REMOVE, CC_OPTIONS, CC_VARS,
            CC_BINDINGS, CC_ARRAYS, CC_INTVARS, CC_SHFUNCS, CC_PARAMS,
            CC_ENVVARS, CC_STOPPED, CC_BUILTINS, CC_ALREG, CC_ALGLOB,
            CC_USERS, CC_DISCMDS, CC_EXCMDS, CC_SCALARS, CC_READONLYS,
            CC_SPECIALS, CC_DELETE, CC_NAMED, CC_QUOTEFLAG, CC_EXTCMDS,
            CC_RESWDS, CC_DIRS, CC_EXPANDEXPL, CC_RESERVED,
        ];
        for &m in &primary {
            assert_eq!(m.count_ones(), 1,
                "primary CC_ mask {:#x} must be single bit", m);
        }
        let mut all: u64 = 0;
        for &m in &primary {
            assert_eq!(all & m, 0,
                "CC_ mask {:#x} overlaps with previous flags", m);
            all |= m;
        }
    }

    /// c:148 — CC_EXPANDEXPL lives at bit 30 (NOT 29). Pin the
    /// gap-at-bit-29 because the C source documents the bit-29
    /// hole at c:147. A regen that "fills the gap" would shift
    /// every subsequent flag.
    #[test]
    fn cc_expandexpl_at_bit_30_skips_bit_29() {
        assert_eq!(CC_EXPANDEXPL, 1 << 30,
            "c:148 — CC_EXPANDEXPL must be at bit 30 (bit 29 is the gap)");
        // Verify nothing else IS bit 29
        let all_primary = [
            CC_FILES, CC_COMMPATH, CC_REMOVE, CC_OPTIONS, CC_VARS,
            CC_BINDINGS, CC_ARRAYS, CC_INTVARS, CC_SHFUNCS, CC_PARAMS,
            CC_ENVVARS, CC_STOPPED, CC_BUILTINS, CC_ALREG, CC_ALGLOB,
            CC_USERS, CC_DISCMDS, CC_EXCMDS, CC_SCALARS, CC_READONLYS,
            CC_SPECIALS, CC_DELETE, CC_NAMED, CC_QUOTEFLAG, CC_EXTCMDS,
            CC_RESWDS, CC_DIRS, CC_EXPANDEXPL, CC_RESERVED,
        ];
        let bit_29: u64 = 1 << 29;
        for &m in &all_primary {
            assert_ne!(m, bit_29,
                "no primary mask should occupy bit 29 (the documented gap)");
        }
    }

    /// c:149 — CC_RESERVED is bit 31 (the high bit). Pin the
    /// boundary so a regen that extends into bit 32 (u64 vs i32
    /// confusion) gets caught.
    #[test]
    fn cc_reserved_is_bit_31() {
        assert_eq!(CC_RESERVED, 1u64 << 31);
    }

    /// c:152-158 — Secondary-mask (mask2) flags occupy bits 0-6 of
    /// their OWN namespace. They DELIBERATELY collide with primary
    /// mask values (CC_NOSORT = 1 = CC_FILES) — the dispatcher
    /// routes via the mask vs mask2 field.
    #[test]
    fn secondary_mask_collides_with_primary_by_design() {
        // CC_NOSORT (mask2 bit 0) and CC_FILES (mask bit 0) both = 1
        assert_eq!(CC_NOSORT, CC_FILES,
            "collision is intentional — different mask fields");
        // CC_XORCONT (mask2 bit 1) and CC_COMMPATH (mask bit 1) both = 2
        assert_eq!(CC_XORCONT, CC_COMMPATH);
        // ... but mask2 has its own no-overlap structure
        let secondary = [
            CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT,
            CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
        ];
        let mut all: u64 = 0;
        for &m in &secondary {
            assert_eq!(m.count_ones(), 1,
                "secondary CC_ mask {:#x} must be single bit", m);
            assert_eq!(all & m, 0,
                "secondary {:#x} overlaps within mask2 namespace", m);
            all |= m;
        }
    }

    /// c:76-89 — CCT_* values are sequential (0..13). Pin the
    /// dense layout so a regen that introduces a gap silently
    /// breaks the dispatcher's `(type - CCT_POS)` subtraction.
    #[test]
    fn cct_values_are_sequential_zero_through_thirteen() {
        let in_order = [
            CCT_UNUSED, CCT_POS, CCT_CURSTR, CCT_CURPAT, CCT_WORDSTR,
            CCT_WORDPAT, CCT_CURSUF, CCT_CURPRE, CCT_CURSUB, CCT_CURSUBC,
            CCT_NUMWORDS, CCT_RANGESTR, CCT_RANGEPAT, CCT_QUOTE,
        ];
        for (i, &v) in in_order.iter().enumerate() {
            assert_eq!(v, i as i32,
                "CCT_ at position {} must be {}, got {}", i, i, v);
        }
    }
}