zsh/ported/zle/compctl_h.rs
1//! `compctl.h` port — completion descriptor types + `CC_*` / `CCT_*`
2//! flag constants used by the legacy `compctl` builtin.
3//!
4//! Port of `Src/Zle/compctl.h`. Canonical home for the four typedefs
5//! (`Compctlp`/`Compctl`/`Compcond`/`Patcomp`) and the two flag-bit
6//! families: 30 `CC_*` primary completion-target flags (mask) and 7
7//! `CC_*` secondary flags (mask2), plus 14 `CCT_*` `-x` condition
8//! types.
9//!
10//! C source: 4 typedefs + 4 structs (`compctlp`, `patcomp`,
11//! `compcond`, `compctl`), 14 `CCT_*` constants (c:76-89), 30
12//! primary `CC_*` constants (c:118-149), 7 secondary `CC_*` constants
13//! (c:152-158). 0 functions.
14//!
15//! `compctl.rs` (the .c port) re-exports these via `pub use
16//! super::compctl_h::*;` so existing `cc_flags::FILES` / `cct::POS`
17//! call sites keep compiling alongside the C-canonical
18//! `CC_FILES` / `CCT_POS` names.
19
20// ---------------------------------------------------------------------------
21// `-x` condition type constants (c:76-89).
22// ---------------------------------------------------------------------------
23/// `CCT_UNUSED` constant.
24pub const CCT_UNUSED: i32 = 0; // c:76
25/// `CCT_POS` constant.
26pub const CCT_POS: i32 = 1; // c:77
27/// `CCT_CURSTR` constant.
28pub const CCT_CURSTR: i32 = 2; // c:78
29/// `CCT_CURPAT` constant.
30pub const CCT_CURPAT: i32 = 3; // c:79
31/// `CCT_WORDSTR` constant.
32pub const CCT_WORDSTR: i32 = 4; // c:80
33/// `CCT_WORDPAT` constant.
34pub const CCT_WORDPAT: i32 = 5; // c:81
35/// `CCT_CURSUF` constant.
36pub const CCT_CURSUF: i32 = 6; // c:82
37/// `CCT_CURPRE` constant.
38pub const CCT_CURPRE: i32 = 7; // c:83
39/// `CCT_CURSUB` constant.
40pub const CCT_CURSUB: i32 = 8; // c:84
41/// `CCT_CURSUBC` constant.
42pub const CCT_CURSUBC: i32 = 9; // c:85
43/// `CCT_NUMWORDS` constant.
44pub const CCT_NUMWORDS: i32 = 10; // c:86
45/// `CCT_RANGESTR` constant.
46pub const CCT_RANGESTR: i32 = 11; // c:87
47/// `CCT_RANGEPAT` constant.
48pub const CCT_RANGEPAT: i32 = 12; // c:88
49/// `CCT_QUOTE` constant.
50pub const CCT_QUOTE: i32 = 13; // c:89
51
52// ---------------------------------------------------------------------------
53// Primary completion-target flags (`mask`, c:118-149).
54// Each bit selects one completion-source kind (files, vars, jobs, ...)
55// the compctl spec expands.
56// ---------------------------------------------------------------------------
57/// `CC_FILES` constant.
58pub const CC_FILES: u64 = 1 << 0; // c:118
59/// `CC_COMMPATH` constant.
60pub const CC_COMMPATH: u64 = 1 << 1; // c:119
61/// `CC_REMOVE` constant.
62pub const CC_REMOVE: u64 = 1 << 2; // c:120
63/// `CC_OPTIONS` constant.
64pub const CC_OPTIONS: u64 = 1 << 3; // c:121
65/// `CC_VARS` constant.
66pub const CC_VARS: u64 = 1 << 4; // c:122
67/// `CC_BINDINGS` constant.
68pub const CC_BINDINGS: u64 = 1 << 5; // c:123
69/// `CC_ARRAYS` constant.
70pub const CC_ARRAYS: u64 = 1 << 6; // c:124
71/// `CC_INTVARS` constant.
72pub const CC_INTVARS: u64 = 1 << 7; // c:125
73/// `CC_SHFUNCS` constant.
74pub const CC_SHFUNCS: u64 = 1 << 8; // c:126
75/// `CC_PARAMS` constant.
76pub const CC_PARAMS: u64 = 1 << 9; // c:127
77/// `CC_ENVVARS` constant.
78pub const CC_ENVVARS: u64 = 1 << 10; // c:128
79/// `CC_JOBS` constant.
80pub const CC_JOBS: u64 = 1 << 11; // c:129
81/// `CC_RUNNING` constant.
82pub const CC_RUNNING: u64 = 1 << 12; // c:130
83/// `CC_STOPPED` constant.
84pub const CC_STOPPED: u64 = 1 << 13; // c:131
85/// `CC_BUILTINS` constant.
86pub const CC_BUILTINS: u64 = 1 << 14; // c:132
87/// `CC_ALREG` constant.
88pub const CC_ALREG: u64 = 1 << 15; // c:133
89/// `CC_ALGLOB` constant.
90pub const CC_ALGLOB: u64 = 1 << 16; // c:134
91/// `CC_USERS` constant.
92pub const CC_USERS: u64 = 1 << 17; // c:135
93/// `CC_DISCMDS` constant.
94pub const CC_DISCMDS: u64 = 1 << 18; // c:136
95/// `CC_EXCMDS` constant.
96pub const CC_EXCMDS: u64 = 1 << 19; // c:137
97/// `CC_SCALARS` constant.
98pub const CC_SCALARS: u64 = 1 << 20; // c:138
99/// `CC_READONLYS` constant.
100pub const CC_READONLYS: u64 = 1 << 21; // c:139
101/// `CC_SPECIALS` constant.
102pub const CC_SPECIALS: u64 = 1 << 22; // c:140
103/// `CC_DELETE` constant.
104pub const CC_DELETE: u64 = 1 << 23; // c:141
105/// `CC_NAMED` constant.
106pub const CC_NAMED: u64 = 1 << 24; // c:142
107/// `CC_QUOTEFLAG` constant.
108pub const CC_QUOTEFLAG: u64 = 1 << 25; // c:143
109/// `CC_EXTCMDS` constant.
110pub const CC_EXTCMDS: u64 = 1 << 26; // c:144
111/// `CC_RESWDS` constant.
112pub const CC_RESWDS: u64 = 1 << 27; // c:145
113/// `CC_DIRS` constant.
114pub const CC_DIRS: u64 = 1 << 28; // c:146
115/// `CC_EXPANDEXPL` constant.
116pub const CC_EXPANDEXPL: u64 = 1 << 30; // c:148
117/// `CC_RESERVED` constant.
118pub const CC_RESERVED: u64 = 1 << 31; // c:149
119
120// ---------------------------------------------------------------------------
121// Secondary completion-target flags (`mask2`, c:152-158).
122// ---------------------------------------------------------------------------
123/// `CC_NOSORT` constant.
124pub const CC_NOSORT: u64 = 1 << 0; // c:152
125/// `CC_XORCONT` constant.
126pub const CC_XORCONT: u64 = 1 << 1; // c:153
127/// `CC_CCCONT` constant.
128pub const CC_CCCONT: u64 = 1 << 2; // c:154
129/// `CC_PATCONT` constant.
130pub const CC_PATCONT: u64 = 1 << 3; // c:155
131/// `CC_DEFCONT` constant.
132pub const CC_DEFCONT: u64 = 1 << 4; // c:156
133/// `CC_UNIQCON` constant.
134pub const CC_UNIQCON: u64 = 1 << 5; // c:157
135/// `CC_UNIQALL` constant.
136pub const CC_UNIQALL: u64 = 1 << 6; // c:158
137
138// ---------------------------------------------------------------------------
139// Typedef structs (c:32-115).
140//
141// C uses linked lists threaded through `next` pointers. The Rust
142// port substitutes `Option<Box<...>>` for the same self-referential
143// chain; the linked-list semantics are preserved.
144// ---------------------------------------------------------------------------
145
146/// Port of `struct compctlp` from `Src/Zle/compctl.h:39-42`. Hash
147/// table node entry holding a pointer to the compctl descriptor.
148///
149/// C definition (c:39-42):
150/// ```c
151/// struct compctlp {
152/// struct hashnode node;
153/// Compctl cc;
154/// };
155/// ```
156///
157/// The Rust port omits the `hashnode` head (zshrs's hashtable
158/// machinery threads name+next through a separate scaffold) and
159/// keeps the semantic payload — a pointer to the compctl descriptor.
160#[derive(Debug, Clone)]
161#[allow(non_camel_case_types)]
162pub struct Compctlp {
163 // c:39
164 pub cc: std::sync::Arc<Compctl>, // c:41
165}
166
167/// Port of `struct patcomp` from `Src/Zle/compctl.h:46-50`. Linked-
168/// list node for the pattern-compctl registry (entries created by
169/// `compctl -p PATTERN ...`).
170///
171/// C definition (c:46-50):
172/// ```c
173/// struct patcomp {
174/// Patcomp next;
175/// char *pat;
176/// Compctl cc;
177/// };
178/// ```
179#[derive(Debug, Clone)]
180#[allow(non_camel_case_types)]
181pub struct Patcomp {
182 // c:46
183 pub next: Option<Box<Patcomp>>, // c:47
184 pub pat: String, // c:48
185 pub cc: std::sync::Arc<Compctl>, // c:49
186}
187
188/// Port of `struct compcond` from `Src/Zle/compctl.h:54-74`. The
189/// per-condition descriptor for `compctl -x`.
190///
191/// C definition (c:54-74):
192/// ```c
193/// struct compcond {
194/// Compcond and, or;
195/// int type; /* one of CCT_* */
196/// int n; /* array length */
197/// union {
198/// struct { int *a, *b; } r; /* CCT_POS, CCT_NUMWORDS */
199/// struct { int *p; char **s; } s; /* CCT_CURSTR, CCT_CURPAT, ... */
200/// struct { char **a, **b; } l; /* CCT_RANGESTR, ... */
201/// } u;
202/// };
203/// ```
204///
205/// The Rust port collapses C's `union` into an explicit enum
206/// (`CompcondData`) since Rust unions require unsafe; the dispatch
207/// is by `typ` per the C convention.
208#[derive(Debug, Clone, Default)]
209#[allow(non_camel_case_types)]
210pub struct Compcond {
211 // c:54
212 pub and: Option<Box<Compcond>>, // c:55
213 pub or: Option<Box<Compcond>>, // c:55
214 pub typ: i32, // c:56 (Rust keyword `type`)
215 pub n: i32, // c:57
216 pub u: CompcondData, // c:58 union
217}
218
219/// Port of the anonymous `union { struct r,s,l }` inside `compcond`
220/// at `Src/Zle/compctl.h:58-73`. The C union is dispatched by
221/// `typ` (one of the `CCT_*` constants).
222#[derive(Debug, Clone, Default)]
223#[allow(non_camel_case_types)]
224pub enum CompcondData {
225 // c:58
226 /// Port of `struct { int *a, *b; } r` (c:59-62) — used by
227 /// `CCT_POS`, `CCT_NUMWORDS`.
228 R { a: Vec<i32>, b: Vec<i32> },
229 /// Port of `struct { int *p; char **s; } s` (c:63-66) — used by
230 /// `CCT_CURSTR`, `CCT_CURPAT`, `CCT_CURSUF`, `CCT_CURPRE`,
231 /// `CCT_CURSUB`, `CCT_CURSUBC`, `CCT_WORDSTR`, `CCT_WORDPAT`,
232 /// `CCT_QUOTE`.
233 S { p: Vec<i32>, s: Vec<String> },
234 /// Port of `struct { char **a, **b; } l` (c:68-71) — used by
235 /// `CCT_RANGESTR`, `CCT_RANGEPAT`.
236 L { a: Vec<String>, b: Vec<String> },
237 /// Empty (CCT_UNUSED).
238 #[default]
239 Unused,
240}
241
242/// Port of `struct compctl` from `Src/Zle/compctl.h:93-115`. The
243/// real per-command compctl descriptor — what `compctl name args`
244/// allocates and registers in the `compctltab` hashtable.
245///
246/// C definition (c:93-115) — 22 fields. Field names + types
247/// preserved verbatim; pointer types collapse to `Option<String>` /
248/// `Option<std::sync::Arc<Compctl>>` etc. as appropriate.
249#[derive(Debug, Clone, Default)]
250#[allow(non_camel_case_types)]
251pub struct Compctl {
252 // c:93
253 /// Reference count.
254 pub refc: i32, // c:94
255 /// Next compctl in a `-x` chain.
256 pub next: Option<std::sync::Arc<Compctl>>, // c:95
257 /// Mask of completion-target flags (`CC_*`).
258 pub mask: u64, // c:96
259 /// Secondary mask of completion-target flags (`CC_*`, mask2).
260 pub mask2: u64, // c:96
261 /// `-k` variable name.
262 pub keyvar: Option<String>, // c:97
263 /// `-g` glob pattern.
264 pub glob: Option<String>, // c:98
265 /// `-s` expansion string.
266 pub str: Option<String>, // c:99 (Rust keyword `str`)
267 /// `-K` function name.
268 pub func: Option<String>, // c:100
269 /// `-X` explanation.
270 pub explain: Option<String>, // c:101
271 /// `-y` user-defined description for listing.
272 pub ylist: Option<String>, // c:102
273 /// `-P` prefix.
274 pub prefix: Option<String>, // c:103
275 /// `-S` suffix.
276 pub suffix: Option<String>, // c:103
277 /// `-l` command name to use.
278 pub subcmd: Option<String>, // c:104
279 /// `-1` command name to use.
280 pub substr: Option<String>, // c:105
281 /// `-w` with-directory.
282 pub withd: Option<String>, // c:106
283 /// `-H` history pattern.
284 pub hpat: Option<String>, // c:107
285 /// `-H` number of events to search.
286 pub hnum: i32, // c:108
287 /// `-J`/`-V` group name.
288 pub gname: Option<String>, // c:109
289 /// `-x` first compctl in the chain.
290 pub ext: Option<std::sync::Arc<Compctl>>, // c:110
291 /// `-x` condition for this compctl.
292 pub cond: Option<Box<Compcond>>, // c:111
293 /// `+` xor'ed compctl chain.
294 pub xor: Option<std::sync::Arc<Compctl>>, // c:112
295 /// `-M` matcher control — head of the Cmatcher chain compiled
296 /// from this compctl's match-spec arg.
297 pub matcher: Option<Box<crate::ported::zle::comp_h::Cmatcher>>, // c:113
298 /// `-M` matcher string.
299 pub mstr: Option<String>, // c:114
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305 use crate::ported::zle::zle_main::zle_test_setup;
306
307 /// Verifies CCT_* values per c:76-89.
308 #[test]
309 fn cct_constants_correct() {
310 let _g = crate::test_util::global_state_lock();
311 let _g = zle_test_setup();
312 assert_eq!(CCT_UNUSED, 0);
313 assert_eq!(CCT_POS, 1);
314 assert_eq!(CCT_CURSTR, 2);
315 assert_eq!(CCT_QUOTE, 13);
316 }
317
318 /// Verifies CC_* primary mask values per c:118-149 — single-bit,
319 /// non-overlapping.
320 #[test]
321 fn cc_primary_mask_bits_distinct() {
322 let _g = crate::test_util::global_state_lock();
323 let _g = zle_test_setup();
324 let all = CC_FILES
325 | CC_COMMPATH
326 | CC_REMOVE
327 | CC_OPTIONS
328 | CC_VARS
329 | CC_BINDINGS
330 | CC_ARRAYS
331 | CC_INTVARS
332 | CC_SHFUNCS
333 | CC_PARAMS
334 | CC_ENVVARS
335 | CC_JOBS
336 | CC_RUNNING
337 | CC_STOPPED
338 | CC_BUILTINS
339 | CC_ALREG
340 | CC_ALGLOB
341 | CC_USERS
342 | CC_DISCMDS
343 | CC_EXCMDS
344 | CC_SCALARS
345 | CC_READONLYS
346 | CC_SPECIALS
347 | CC_DELETE
348 | CC_NAMED
349 | CC_QUOTEFLAG
350 | CC_EXTCMDS
351 | CC_RESWDS
352 | CC_DIRS
353 | CC_EXPANDEXPL
354 | CC_RESERVED;
355 assert_eq!(all.count_ones(), 31); // 30 sequential + 30 + 31 (skips bit 29)
356 }
357
358 /// Verifies the secondary mask values per c:152-158.
359 #[test]
360 fn cc_secondary_mask_values() {
361 let _g = crate::test_util::global_state_lock();
362 let _g = zle_test_setup();
363 assert_eq!(CC_NOSORT, 1);
364 assert_eq!(CC_XORCONT, 2);
365 assert_eq!(CC_UNIQALL, 1 << 6);
366 }
367
368 /// Verifies Compctl Default initialiser zeroes every field per
369 /// the C convention of `(Compctl) calloc(1, sizeof(...))`.
370 #[test]
371 fn compctl_default_zeros_fields() {
372 let _g = crate::test_util::global_state_lock();
373 let _g = zle_test_setup();
374 let cc = Compctl::default();
375 assert_eq!(cc.refc, 0);
376 assert!(cc.next.is_none());
377 assert_eq!(cc.mask, 0);
378 assert_eq!(cc.mask2, 0);
379 assert!(cc.keyvar.is_none());
380 assert!(cc.cond.is_none());
381 assert!(cc.xor.is_none());
382 assert_eq!(cc.hnum, 0);
383 }
384
385 /// Verifies Compcond Default starts in CCT_UNUSED state.
386 #[test]
387 fn compcond_default_is_unused() {
388 let _g = crate::test_util::global_state_lock();
389 let _g = zle_test_setup();
390 let c = Compcond::default();
391 assert_eq!(c.typ, CCT_UNUSED);
392 assert!(matches!(c.u, CompcondData::Unused));
393 }
394
395 /// Verifies the CompcondData variants align with the C union
396 /// dispatch per c:58-73.
397 #[test]
398 fn compcond_data_variants() {
399 let _g = crate::test_util::global_state_lock();
400 let _g = zle_test_setup();
401 let r = CompcondData::R {
402 a: vec![0, 1],
403 b: vec![2, 3],
404 };
405 if let CompcondData::R { a, b } = r {
406 assert_eq!(a, vec![0, 1]);
407 assert_eq!(b, vec![2, 3]);
408 } else {
409 panic!("expected R variant");
410 }
411 let s = CompcondData::S {
412 p: vec![1],
413 s: vec!["x".into()],
414 };
415 assert!(matches!(s, CompcondData::S { .. }));
416 let l = CompcondData::L {
417 a: vec!["lo".into()],
418 b: vec!["hi".into()],
419 };
420 assert!(matches!(l, CompcondData::L { .. }));
421 }
422
423 /// c:76-89 — Every CCT_* constant is a unique non-negative
424 /// integer. Pin uniqueness so a copy-paste regen doesn't double
425 /// up a tag (which would silently route two different
426 /// completion-condition kinds through the same dispatch arm).
427 #[test]
428 fn cct_constants_are_unique() {
429 let _g = crate::test_util::global_state_lock();
430 let _g = zle_test_setup();
431 let all = [
432 CCT_UNUSED,
433 CCT_POS,
434 CCT_CURSTR,
435 CCT_CURPAT,
436 CCT_WORDSTR,
437 CCT_WORDPAT,
438 CCT_CURSUF,
439 CCT_CURPRE,
440 CCT_CURSUB,
441 CCT_CURSUBC,
442 CCT_NUMWORDS,
443 CCT_RANGESTR,
444 CCT_RANGEPAT,
445 CCT_QUOTE,
446 ];
447 let unique: std::collections::HashSet<_> = all.iter().copied().collect();
448 assert_eq!(unique.len(), all.len(), "duplicate CCT_* constant detected");
449 for &v in &all {
450 assert!(v >= 0, "CCT_* constants must be non-negative");
451 }
452 }
453
454 /// c:76 — CCT_UNUSED MUST be 0. The C source uses zero-init as
455 /// the implicit "no condition" sentinel; a regression that sets
456 /// CCT_UNUSED = 1 would mark every just-allocated condition as
457 /// CCT_POS by accident.
458 #[test]
459 fn cct_unused_is_zero() {
460 let _g = crate::test_util::global_state_lock();
461 assert_eq!(CCT_UNUSED, 0, "CCT_UNUSED must be the zero-init sentinel");
462 }
463
464 /// c:118-127 — CC_* primary-mask bits are distinct singletons.
465 /// Pin the bit-packing because the c:152 "primary mask" vs
466 /// c:154-158 "secondary mask" distinction relies on the primary
467 /// bits all being non-overlapping.
468 #[test]
469 fn cc_primary_mask_bits_are_distinct_singletons() {
470 let _g = crate::test_util::global_state_lock();
471 let primary = [
472 CC_FILES,
473 CC_COMMPATH,
474 CC_REMOVE,
475 CC_OPTIONS,
476 CC_VARS,
477 CC_BINDINGS,
478 CC_ARRAYS,
479 CC_INTVARS,
480 CC_SHFUNCS,
481 CC_PARAMS,
482 CC_ENVVARS,
483 ];
484 for &m in &primary {
485 assert_eq!(
486 m.count_ones(),
487 1,
488 "primary CC_ mask {} has {} bits set",
489 m,
490 m.count_ones()
491 );
492 }
493 let mut all: u64 = 0;
494 for &m in &primary {
495 assert_eq!(all & m, 0, "primary CC_ mask {} overlaps", m);
496 all |= m;
497 }
498 }
499
500 /// c:108 — `Compctl::default()` Default impl produces an empty
501 /// struct ready for population. After a default + manual field
502 /// write, the other fields must remain at their zero-init values.
503 #[test]
504 fn compctl_default_partial_population_doesnt_clobber_other_fields() {
505 let _g = crate::test_util::global_state_lock();
506 let _g = zle_test_setup();
507 let mut cc = Compctl::default();
508 cc.mask = CC_FILES;
509 assert_eq!(cc.mask, CC_FILES);
510 // Every OTHER field must still be at default
511 assert_eq!(cc.refc, 0);
512 assert!(cc.next.is_none());
513 assert_eq!(cc.mask2, 0);
514 assert!(cc.keyvar.is_none());
515 assert!(cc.cond.is_none());
516 assert_eq!(cc.hnum, 0);
517 }
518
519 /// `Compcond::default()` produces a CCT_UNUSED node with the
520 /// `Unused` data variant. Pin the simultaneous shape so a
521 /// regression that picks one but not the other (e.g. CCT_POS
522 /// with Unused data) gets caught.
523 #[test]
524 fn compcond_default_typ_and_data_are_consistent() {
525 let _g = crate::test_util::global_state_lock();
526 let _g = zle_test_setup();
527 let c = Compcond::default();
528 assert_eq!(c.typ, CCT_UNUSED, "tag must be UNUSED");
529 assert!(
530 matches!(c.u, CompcondData::Unused),
531 "data must be CompcondData::Unused"
532 );
533 }
534
535 /// c:118-149 — Full sweep of CC_* primary-mask bits 0..31. Each
536 /// must occupy a distinct bit position. Catches a regen that
537 /// renumbers two flags to the same shift.
538 #[test]
539 fn cc_primary_mask_full_sweep_no_overlap() {
540 let _g = crate::test_util::global_state_lock();
541 let primary = [
542 CC_FILES,
543 CC_COMMPATH,
544 CC_REMOVE,
545 CC_OPTIONS,
546 CC_VARS,
547 CC_BINDINGS,
548 CC_ARRAYS,
549 CC_INTVARS,
550 CC_SHFUNCS,
551 CC_PARAMS,
552 CC_ENVVARS,
553 CC_STOPPED,
554 CC_BUILTINS,
555 CC_ALREG,
556 CC_ALGLOB,
557 CC_USERS,
558 CC_DISCMDS,
559 CC_EXCMDS,
560 CC_SCALARS,
561 CC_READONLYS,
562 CC_SPECIALS,
563 CC_DELETE,
564 CC_NAMED,
565 CC_QUOTEFLAG,
566 CC_EXTCMDS,
567 CC_RESWDS,
568 CC_DIRS,
569 CC_EXPANDEXPL,
570 CC_RESERVED,
571 ];
572 for &m in &primary {
573 assert_eq!(
574 m.count_ones(),
575 1,
576 "primary CC_ mask {:#x} must be single bit",
577 m
578 );
579 }
580 let mut all: u64 = 0;
581 for &m in &primary {
582 assert_eq!(all & m, 0, "CC_ mask {:#x} overlaps with previous flags", m);
583 all |= m;
584 }
585 }
586
587 /// c:148 — CC_EXPANDEXPL lives at bit 30 (NOT 29). Pin the
588 /// gap-at-bit-29 because the C source documents the bit-29
589 /// hole at c:147. A regen that "fills the gap" would shift
590 /// every subsequent flag.
591 #[test]
592 fn cc_expandexpl_at_bit_30_skips_bit_29() {
593 let _g = crate::test_util::global_state_lock();
594 assert_eq!(
595 CC_EXPANDEXPL,
596 1 << 30,
597 "c:148 — CC_EXPANDEXPL must be at bit 30 (bit 29 is the gap)"
598 );
599 // Verify nothing else IS bit 29
600 let all_primary = [
601 CC_FILES,
602 CC_COMMPATH,
603 CC_REMOVE,
604 CC_OPTIONS,
605 CC_VARS,
606 CC_BINDINGS,
607 CC_ARRAYS,
608 CC_INTVARS,
609 CC_SHFUNCS,
610 CC_PARAMS,
611 CC_ENVVARS,
612 CC_STOPPED,
613 CC_BUILTINS,
614 CC_ALREG,
615 CC_ALGLOB,
616 CC_USERS,
617 CC_DISCMDS,
618 CC_EXCMDS,
619 CC_SCALARS,
620 CC_READONLYS,
621 CC_SPECIALS,
622 CC_DELETE,
623 CC_NAMED,
624 CC_QUOTEFLAG,
625 CC_EXTCMDS,
626 CC_RESWDS,
627 CC_DIRS,
628 CC_EXPANDEXPL,
629 CC_RESERVED,
630 ];
631 let bit_29: u64 = 1 << 29;
632 for &m in &all_primary {
633 assert_ne!(
634 m, bit_29,
635 "no primary mask should occupy bit 29 (the documented gap)"
636 );
637 }
638 }
639
640 /// c:149 — CC_RESERVED is bit 31 (the high bit). Pin the
641 /// boundary so a regen that extends into bit 32 (u64 vs i32
642 /// confusion) gets caught.
643 #[test]
644 fn cc_reserved_is_bit_31() {
645 let _g = crate::test_util::global_state_lock();
646 assert_eq!(CC_RESERVED, 1u64 << 31);
647 }
648
649 /// c:152-158 — Secondary-mask (mask2) flags occupy bits 0-6 of
650 /// their OWN namespace. They DELIBERATELY collide with primary
651 /// mask values (CC_NOSORT = 1 = CC_FILES) — the dispatcher
652 /// routes via the mask vs mask2 field.
653 #[test]
654 fn secondary_mask_collides_with_primary_by_design() {
655 let _g = crate::test_util::global_state_lock();
656 // CC_NOSORT (mask2 bit 0) and CC_FILES (mask bit 0) both = 1
657 assert_eq!(
658 CC_NOSORT, CC_FILES,
659 "collision is intentional — different mask fields"
660 );
661 // CC_XORCONT (mask2 bit 1) and CC_COMMPATH (mask bit 1) both = 2
662 assert_eq!(CC_XORCONT, CC_COMMPATH);
663 // ... but mask2 has its own no-overlap structure
664 let secondary = [
665 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
666 ];
667 let mut all: u64 = 0;
668 for &m in &secondary {
669 assert_eq!(
670 m.count_ones(),
671 1,
672 "secondary CC_ mask {:#x} must be single bit",
673 m
674 );
675 assert_eq!(
676 all & m,
677 0,
678 "secondary {:#x} overlaps within mask2 namespace",
679 m
680 );
681 all |= m;
682 }
683 }
684
685 /// c:76-89 — CCT_* values are sequential (0..13). Pin the
686 /// dense layout so a regen that introduces a gap silently
687 /// breaks the dispatcher's `(type - CCT_POS)` subtraction.
688 #[test]
689 fn cct_values_are_sequential_zero_through_thirteen() {
690 let _g = crate::test_util::global_state_lock();
691 let in_order = [
692 CCT_UNUSED,
693 CCT_POS,
694 CCT_CURSTR,
695 CCT_CURPAT,
696 CCT_WORDSTR,
697 CCT_WORDPAT,
698 CCT_CURSUF,
699 CCT_CURPRE,
700 CCT_CURSUB,
701 CCT_CURSUBC,
702 CCT_NUMWORDS,
703 CCT_RANGESTR,
704 CCT_RANGEPAT,
705 CCT_QUOTE,
706 ];
707 for (i, &v) in in_order.iter().enumerate() {
708 assert_eq!(
709 v, i as i32,
710 "CCT_ at position {} must be {}, got {}",
711 i, i, v
712 );
713 }
714 }
715
716 // ═══════════════════════════════════════════════════════════════════
717 // Additional C-parity tests for Src/Zle/compctl.h constants.
718 // ═══════════════════════════════════════════════════════════════════
719
720 /// c:76-89 — CCT_* values are sequential 0..14 (no holes).
721 #[test]
722 fn cct_values_pairwise_distinct() {
723 let all = [
724 CCT_UNUSED,
725 CCT_POS,
726 CCT_CURSTR,
727 CCT_CURPAT,
728 CCT_WORDSTR,
729 CCT_WORDPAT,
730 CCT_CURSUF,
731 CCT_CURPRE,
732 CCT_CURSUB,
733 CCT_CURSUBC,
734 CCT_NUMWORDS,
735 CCT_RANGESTR,
736 CCT_RANGEPAT,
737 CCT_QUOTE,
738 ];
739 let unique: std::collections::HashSet<_> = all.iter().copied().collect();
740 assert_eq!(unique.len(), all.len(), "CCT_* must be pairwise distinct");
741 }
742
743 /// c:76-89 — CCT_* canonical mid-table values (spot-check).
744 #[test]
745 fn cct_canonical_mid_values() {
746 assert_eq!(CCT_WORDSTR, 4);
747 assert_eq!(CCT_WORDPAT, 5);
748 assert_eq!(CCT_CURSUF, 6);
749 assert_eq!(CCT_CURPRE, 7);
750 assert_eq!(CCT_NUMWORDS, 10);
751 assert_eq!(CCT_RANGESTR, 11);
752 assert_eq!(CCT_RANGEPAT, 12);
753 }
754
755 /// c:118 — CC_FILES is bit 0 (the most common file-completion flag,
756 /// hot-path bit). Pin so a regen flipping bit assignments doesn't
757 /// silently break Tab completion of filenames.
758 #[test]
759 fn cc_files_is_bit_zero() {
760 assert_eq!(CC_FILES, 1);
761 }
762
763 /// c:118-128 — first 10 CC_* mask bits are pairwise disjoint
764 /// single-bit values.
765 #[test]
766 fn cc_first_10_flags_are_pairwise_disjoint_single_bits() {
767 let flags = [
768 CC_FILES,
769 CC_COMMPATH,
770 CC_REMOVE,
771 CC_OPTIONS,
772 CC_VARS,
773 CC_BINDINGS,
774 CC_ARRAYS,
775 CC_INTVARS,
776 CC_SHFUNCS,
777 CC_PARAMS,
778 ];
779 for (i, &a) in flags.iter().enumerate() {
780 assert!(a.is_power_of_two(), "CC_* flag {} must be single bit", a);
781 for &b in flags.iter().skip(i + 1) {
782 assert_eq!(a & b, 0, "{} and {} overlap", a, b);
783 }
784 }
785 }
786
787 /// c:118-128 — first 10 CC_* mask bits in canonical 1<<N positions.
788 #[test]
789 fn cc_first_10_flags_canonical_positions() {
790 assert_eq!(CC_FILES, 1 << 0);
791 assert_eq!(CC_COMMPATH, 1 << 1);
792 assert_eq!(CC_REMOVE, 1 << 2);
793 assert_eq!(CC_OPTIONS, 1 << 3);
794 assert_eq!(CC_VARS, 1 << 4);
795 assert_eq!(CC_BINDINGS, 1 << 5);
796 assert_eq!(CC_ARRAYS, 1 << 6);
797 assert_eq!(CC_INTVARS, 1 << 7);
798 assert_eq!(CC_SHFUNCS, 1 << 8);
799 assert_eq!(CC_PARAMS, 1 << 9);
800 }
801
802 /// c:152-158 — CC_NOSORT/XORCONT canonical positions in secondary
803 /// mask namespace (separate u64 from CC_FILES etc.).
804 #[test]
805 fn cc_secondary_low_bits_canonical() {
806 assert_eq!(CC_NOSORT, 1);
807 assert_eq!(CC_XORCONT, 2);
808 }
809
810 /// c:155 — CC_UNIQALL = 1 << 6.
811 #[test]
812 fn cc_uniqall_is_bit_6_in_secondary() {
813 assert_eq!(CC_UNIQALL, 1 << 6);
814 }
815
816 // ═══════════════════════════════════════════════════════════════════
817 // Additional C-parity tests for Src/Zle/compctl.h c:131-156 CC_*
818 // ═══════════════════════════════════════════════════════════════════
819
820 /// c:131-149 — every primary CC_* flag value is a power of two.
821 #[test]
822 fn cc_primary_flags_all_powers_of_two_full_sweep() {
823 for &v in &[
824 CC_STOPPED,
825 CC_BUILTINS,
826 CC_ALREG,
827 CC_ALGLOB,
828 CC_USERS,
829 CC_DISCMDS,
830 CC_EXCMDS,
831 CC_SCALARS,
832 CC_READONLYS,
833 CC_SPECIALS,
834 CC_DELETE,
835 CC_NAMED,
836 CC_QUOTEFLAG,
837 CC_EXTCMDS,
838 CC_RESWDS,
839 CC_DIRS,
840 CC_EXPANDEXPL,
841 CC_RESERVED,
842 ] {
843 assert!(
844 v.is_power_of_two(),
845 "CC_* primary {:#x} must be single bit",
846 v
847 );
848 }
849 }
850
851 /// c:148 — CC_EXPANDEXPL = bit 30 (skips bit 29 by design).
852 #[test]
853 fn cc_expandexpl_skips_bit_29() {
854 assert_eq!(CC_EXPANDEXPL, 1u64 << 30, "c:148 = bit 30");
855 // Bit 29 is reserved (intentionally unused per C source).
856 }
857
858 /// c:152-158 — secondary CC_* flags are powers of two.
859 #[test]
860 fn cc_secondary_flags_all_powers_of_two() {
861 for &v in &[
862 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
863 ] {
864 assert!(
865 v.is_power_of_two(),
866 "CC_* secondary {:#x} must be single bit",
867 v
868 );
869 }
870 }
871
872 /// c:152-158 — secondary CC_* are pairwise distinct.
873 #[test]
874 fn cc_secondary_flags_pairwise_distinct() {
875 let codes = [
876 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
877 ];
878 let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
879 assert_eq!(unique.len(), codes.len(), "CC_* secondary must be distinct");
880 }
881
882 /// c:131-149 — primary CC_* canonical bit positions match c:113-149.
883 #[test]
884 fn cc_primary_canonical_high_bit_positions() {
885 assert_eq!(CC_DIRS, 1u64 << 28, "c:146");
886 assert_eq!(CC_RESERVED, 1u64 << 31, "c:149");
887 assert_eq!(CC_DELETE, 1u64 << 23, "c:141");
888 assert_eq!(CC_NAMED, 1u64 << 24, "c:142");
889 assert_eq!(CC_USERS, 1u64 << 17, "c:135");
890 }
891
892 /// c:131-149 + c:152-158 — every CC_* fits in u64 (compile-time
893 /// type pin).
894 #[test]
895 fn cc_all_flags_are_u64_type() {
896 let _: u64 = CC_STOPPED;
897 let _: u64 = CC_NOSORT;
898 let _: u64 = CC_RESERVED;
899 }
900
901 /// c:152-158 — CC_NOSORT through CC_UNIQALL form contiguous low-7 bits.
902 #[test]
903 fn cc_secondary_form_contiguous_low_bits() {
904 let mut bits: Vec<u64> = vec![
905 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
906 ];
907 bits.sort();
908 for (i, &b) in bits.iter().enumerate() {
909 assert_eq!(b, 1u64 << i, "secondary bit {} must be 1<<{}", i, i);
910 }
911 }
912
913 /// c:131-149 — primary CC_* form mostly contiguous from bit 0 up
914 /// (with bit 29 reserved gap to bit 30 EXPANDEXPL).
915 #[test]
916 fn cc_primary_no_gaps_through_bit_28() {
917 // Bits 0..28 must all be claimed by primary CC_* flags.
918 let all = [
919 CC_FILES,
920 CC_COMMPATH,
921 CC_REMOVE,
922 CC_OPTIONS,
923 CC_VARS,
924 CC_BINDINGS,
925 CC_ARRAYS,
926 CC_INTVARS,
927 CC_SHFUNCS,
928 CC_PARAMS,
929 CC_ENVVARS,
930 CC_JOBS,
931 CC_RUNNING,
932 CC_STOPPED,
933 CC_BUILTINS,
934 CC_ALREG,
935 CC_ALGLOB,
936 CC_USERS,
937 CC_DISCMDS,
938 CC_EXCMDS,
939 CC_SCALARS,
940 CC_READONLYS,
941 CC_SPECIALS,
942 CC_DELETE,
943 CC_NAMED,
944 CC_QUOTEFLAG,
945 CC_EXTCMDS,
946 CC_RESWDS,
947 CC_DIRS,
948 ];
949 let or_all: u64 = all.iter().fold(0, |acc, &v| acc | v);
950 assert_eq!(or_all, (1u64 << 29) - 1, "bits 0..28 must all be set");
951 }
952
953 // ═══════════════════════════════════════════════════════════════════
954 // Additional C-parity tests for Src/Zle/compctl.h
955 // c:76-89 CCT_* / c:118-149 primary CC_* / c:152-158 secondary CC_*
956 // ═══════════════════════════════════════════════════════════════════
957
958 /// c:76 — `CCT_UNUSED` is the zero sentinel (slot 0).
959 #[test]
960 fn cct_unused_is_zero_sentinel() {
961 assert_eq!(CCT_UNUSED, 0, "c:76 — UNUSED is the slot-0 sentinel");
962 }
963
964 /// c:76-89 — all CCT_* codes are i32 (compile-time type pin).
965 #[test]
966 fn cct_codes_all_i32_type() {
967 let _: i32 = CCT_UNUSED;
968 let _: i32 = CCT_POS;
969 let _: i32 = CCT_QUOTE;
970 }
971
972 /// c:76-89 — CCT_* codes cover slots 0..=13 with no gaps/dups.
973 #[test]
974 fn cct_codes_dense_0_through_13() {
975 let mut codes = vec![
976 CCT_UNUSED,
977 CCT_POS,
978 CCT_CURSTR,
979 CCT_CURPAT,
980 CCT_WORDSTR,
981 CCT_WORDPAT,
982 CCT_CURSUF,
983 CCT_CURPRE,
984 CCT_CURSUB,
985 CCT_CURSUBC,
986 CCT_NUMWORDS,
987 CCT_RANGESTR,
988 CCT_RANGEPAT,
989 CCT_QUOTE,
990 ];
991 codes.sort();
992 let expected: Vec<i32> = (0..=13).collect();
993 assert_eq!(
994 codes, expected,
995 "CCT_* must cover slots 0..=13 with no gaps/dups"
996 );
997 }
998
999 /// c:118 — `CC_FILES` is canonical bit 0 (alt name pin).
1000 #[test]
1001 fn cc_files_is_bit_zero_alt() {
1002 assert_eq!(CC_FILES, 1u64 << 0, "c:118 — FILES is bit 0");
1003 }
1004
1005 /// c:131-149 — primary CC_* form a complete bitset:
1006 /// (bits 0..28) | EXPANDEXPL(30) | RESERVED(31), with bit 29 gap.
1007 #[test]
1008 fn cc_primary_complete_bitset_with_bit29_gap() {
1009 let all = [
1010 CC_FILES,
1011 CC_COMMPATH,
1012 CC_REMOVE,
1013 CC_OPTIONS,
1014 CC_VARS,
1015 CC_BINDINGS,
1016 CC_ARRAYS,
1017 CC_INTVARS,
1018 CC_SHFUNCS,
1019 CC_PARAMS,
1020 CC_ENVVARS,
1021 CC_JOBS,
1022 CC_RUNNING,
1023 CC_STOPPED,
1024 CC_BUILTINS,
1025 CC_ALREG,
1026 CC_ALGLOB,
1027 CC_USERS,
1028 CC_DISCMDS,
1029 CC_EXCMDS,
1030 CC_SCALARS,
1031 CC_READONLYS,
1032 CC_SPECIALS,
1033 CC_DELETE,
1034 CC_NAMED,
1035 CC_QUOTEFLAG,
1036 CC_EXTCMDS,
1037 CC_RESWDS,
1038 CC_DIRS,
1039 CC_EXPANDEXPL,
1040 CC_RESERVED,
1041 ];
1042 let or_all: u64 = all.iter().fold(0, |acc, &v| acc | v);
1043 let expected = ((1u64 << 29) - 1) | (1u64 << 30) | (1u64 << 31);
1044 assert_eq!(
1045 or_all, expected,
1046 "primary CC_* must cover bits 0..28 + 30 + 31 (bit 29 reserved)"
1047 );
1048 }
1049
1050 /// c:131-149 — primary CC_* are pairwise distinct.
1051 #[test]
1052 fn cc_primary_flags_pairwise_distinct() {
1053 let codes = [
1054 CC_FILES,
1055 CC_COMMPATH,
1056 CC_REMOVE,
1057 CC_OPTIONS,
1058 CC_VARS,
1059 CC_BINDINGS,
1060 CC_ARRAYS,
1061 CC_INTVARS,
1062 CC_SHFUNCS,
1063 CC_PARAMS,
1064 CC_ENVVARS,
1065 CC_JOBS,
1066 CC_RUNNING,
1067 CC_STOPPED,
1068 CC_BUILTINS,
1069 CC_ALREG,
1070 CC_ALGLOB,
1071 CC_USERS,
1072 CC_DISCMDS,
1073 CC_EXCMDS,
1074 CC_SCALARS,
1075 CC_READONLYS,
1076 CC_SPECIALS,
1077 CC_DELETE,
1078 CC_NAMED,
1079 CC_QUOTEFLAG,
1080 CC_EXTCMDS,
1081 CC_RESWDS,
1082 CC_DIRS,
1083 CC_EXPANDEXPL,
1084 CC_RESERVED,
1085 ];
1086 let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1087 assert_eq!(
1088 unique.len(),
1089 codes.len(),
1090 "primary CC_* must be pairwise distinct"
1091 );
1092 }
1093
1094 /// c:118-149 — every primary CC_* is non-zero (no false sentinel).
1095 #[test]
1096 fn cc_primary_all_non_zero() {
1097 for &v in &[
1098 CC_FILES,
1099 CC_COMMPATH,
1100 CC_REMOVE,
1101 CC_OPTIONS,
1102 CC_DIRS,
1103 CC_EXPANDEXPL,
1104 CC_RESERVED,
1105 ] {
1106 assert!(v > 0, "CC_* primary must be > 0; got {}", v);
1107 }
1108 }
1109
1110 /// c:152-158 — every secondary CC_* is non-zero.
1111 #[test]
1112 fn cc_secondary_all_non_zero() {
1113 for &v in &[
1114 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
1115 ] {
1116 assert!(v > 0, "CC_* secondary must be > 0; got {}", v);
1117 }
1118 }
1119
1120 // ═══════════════════════════════════════════════════════════════════
1121 // Additional C-parity pins for Src/Zle/compctl.h
1122 // c:76-89 CCT_* / c:118-149 CC_* / c:152-158 secondary CC_*
1123 // ═══════════════════════════════════════════════════════════════════
1124
1125 /// c:76-89 — every CCT_* is in 0..=13 (no out-of-range).
1126 #[test]
1127 fn cct_all_in_range_0_to_13() {
1128 for &v in &[
1129 CCT_UNUSED,
1130 CCT_POS,
1131 CCT_CURSTR,
1132 CCT_CURPAT,
1133 CCT_WORDSTR,
1134 CCT_WORDPAT,
1135 CCT_CURSUF,
1136 CCT_CURPRE,
1137 CCT_CURSUB,
1138 CCT_CURSUBC,
1139 CCT_NUMWORDS,
1140 CCT_RANGESTR,
1141 CCT_RANGEPAT,
1142 CCT_QUOTE,
1143 ] {
1144 assert!((0..=13).contains(&v), "CCT_* must be 0..=13, got {}", v);
1145 }
1146 }
1147
1148 /// c:76-89 — CCT_* values are pairwise distinct (sequential).
1149 #[test]
1150 fn cct_pairwise_distinct() {
1151 let codes = [
1152 CCT_UNUSED,
1153 CCT_POS,
1154 CCT_CURSTR,
1155 CCT_CURPAT,
1156 CCT_WORDSTR,
1157 CCT_WORDPAT,
1158 CCT_CURSUF,
1159 CCT_CURPRE,
1160 CCT_CURSUB,
1161 CCT_CURSUBC,
1162 CCT_NUMWORDS,
1163 CCT_RANGESTR,
1164 CCT_RANGEPAT,
1165 CCT_QUOTE,
1166 ];
1167 let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1168 assert_eq!(unique.len(), codes.len(), "CCT_* pairwise distinct");
1169 }
1170
1171 /// c:76 — `CCT_UNUSED` is sentinel zero (alt pin).
1172 #[test]
1173 fn cct_unused_is_zero_sentinel_alt() {
1174 assert_eq!(CCT_UNUSED, 0, "CCT_UNUSED must be 0 (sentinel)");
1175 }
1176
1177 /// c:148 — `CC_EXPANDEXPL` skips bit 29 (gap preserved from C).
1178 #[test]
1179 fn cc_expandexpl_uses_bit_30_not_29() {
1180 assert_eq!(
1181 CC_EXPANDEXPL,
1182 1u64 << 30,
1183 "CC_EXPANDEXPL must use bit 30 (bit 29 reserved)"
1184 );
1185 }
1186
1187 /// c:152-158 — every secondary CC_* is in low 7 bits (0..7).
1188 #[test]
1189 fn cc_secondary_in_low_7_bits() {
1190 for &v in &[
1191 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
1192 ] {
1193 assert!(
1194 v < (1u64 << 7),
1195 "CC_* secondary must be < (1<<7), got 0x{:x}",
1196 v
1197 );
1198 }
1199 }
1200
1201 /// c:118-149 — bit 29 is the reserved gap; no primary CC_* uses it.
1202 #[test]
1203 fn cc_primary_bit_29_is_reserved_gap() {
1204 let codes = [
1205 CC_FILES,
1206 CC_COMMPATH,
1207 CC_REMOVE,
1208 CC_OPTIONS,
1209 CC_VARS,
1210 CC_BINDINGS,
1211 CC_ARRAYS,
1212 CC_INTVARS,
1213 CC_SHFUNCS,
1214 CC_PARAMS,
1215 CC_ENVVARS,
1216 CC_JOBS,
1217 CC_RUNNING,
1218 CC_STOPPED,
1219 CC_BUILTINS,
1220 CC_ALREG,
1221 CC_ALGLOB,
1222 CC_USERS,
1223 CC_DISCMDS,
1224 CC_EXCMDS,
1225 CC_SCALARS,
1226 CC_READONLYS,
1227 CC_SPECIALS,
1228 CC_DELETE,
1229 CC_NAMED,
1230 CC_QUOTEFLAG,
1231 CC_EXTCMDS,
1232 CC_RESWDS,
1233 CC_DIRS,
1234 CC_EXPANDEXPL,
1235 CC_RESERVED,
1236 ];
1237 for c in codes {
1238 assert_ne!(
1239 c,
1240 1u64 << 29,
1241 "no primary CC_* uses bit 29 (reserved); offending: 0x{:x}",
1242 c
1243 );
1244 }
1245 }
1246
1247 /// c:152-158 — secondary CC_* pairwise distinct.
1248 #[test]
1249 fn cc_secondary_pairwise_distinct() {
1250 let codes = [
1251 CC_NOSORT, CC_XORCONT, CC_CCCONT, CC_PATCONT, CC_DEFCONT, CC_UNIQCON, CC_UNIQALL,
1252 ];
1253 let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1254 assert_eq!(
1255 unique.len(),
1256 codes.len(),
1257 "secondary CC_* pairwise distinct"
1258 );
1259 }
1260
1261 /// c:152-158 — OR of all secondary CC_* covers bits 0..6.
1262 #[test]
1263 fn cc_secondary_or_covers_low_7_bits() {
1264 let or_all =
1265 CC_NOSORT | CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT | CC_UNIQCON | CC_UNIQALL;
1266 assert_eq!(
1267 or_all,
1268 (1u64 << 7) - 1,
1269 "secondary CC_* must cover bits 0..6"
1270 );
1271 }
1272
1273 /// c:118 — `CC_FILES` is bit 0 (smallest primary, pin alt2).
1274 #[test]
1275 fn cc_files_is_bit_zero_pin_alt2() {
1276 assert_eq!(CC_FILES, 1u64, "CC_FILES must be bit 0");
1277 }
1278
1279 /// c:152 — `CC_NOSORT` is bit 0 (smallest secondary).
1280 #[test]
1281 fn cc_nosort_is_bit_zero() {
1282 assert_eq!(CC_NOSORT, 1u64, "CC_NOSORT must be bit 0 of secondary mask");
1283 }
1284
1285 /// c:149 — `CC_RESERVED` is the highest primary (bit 31).
1286 #[test]
1287 fn cc_reserved_is_highest_bit_31() {
1288 assert_eq!(CC_RESERVED, 1u64 << 31, "CC_RESERVED must be bit 31");
1289 }
1290
1291 /// c:158 — `CC_UNIQALL` is the highest secondary (bit 6).
1292 #[test]
1293 fn cc_uniqall_is_bit_six() {
1294 assert_eq!(CC_UNIQALL, 1u64 << 6, "CC_UNIQALL must be bit 6");
1295 }
1296
1297 /// c:78 — `CCT_CURSTR` = 2 (positional value pin).
1298 #[test]
1299 fn cct_curstr_is_two() {
1300 assert_eq!(CCT_CURSTR, 2);
1301 }
1302}