code wiki / _hdl_build / nx_srclint.nx

nx_srclint.nx source

↩ module page · 761 lines · 40604 B

1// nx_srclint.nx -- LINT THE HAZARDS THAT HAVE ACTUALLY BITTEN, ESTATE-WIDE. 2// 3// RULE 1: CURSOR-SENTINEL. The idiom 4// 5// while p < n { if <done> { p = n + 9 } ... } 6// if p > n { p = p - 9 } 7// 8// writes the loop's EXIT MARKER INTO THE VERY VARIABLE BEING SEARCHED FOR, and the restore lands on 9// `n`, not on the position found. It does not crash and it does not fail to compile -- it silently 10// returns the wrong index, so the caller reads the wrong byte and every downstream number is plausible 11// and wrong. Measured cost on 2026-08-07 alone: FIVE defects. 12// 13// nx_memdemote -- the whole file parsed as ONE line 14// nx_memnew -- file_path extraction returned nothing, the hook silently never fired 15// nx_memnew -- the memfind output parsed as one line, zero results shown 16// nx_unwired -- every identifier ran to end-of-file: 0 functions found across 7,113 sources 17// nx_gatesubj -- the char after an identifier read from the wrong place: 0 of 157 end-to-end 18// gates detected, in the organ built to measure exactly that 19// 20// The fourth and fifth happened INSIDE tools written to catch unverified work, hours after the 21// primitive that fixes it (`gk_eol`) already existed. 22// ★★★★★★ THE FIFTH TIME YOU FIX A BUG BY HAND IS PROOF THE FIX BELONGS IN A LINTER, NOT IN ANOTHER 23// PATCH -- A HAZARD YOU CAN RECOGNISE ON SIGHT IS A HAZARD A MACHINE CAN RECOGNISE EVERY TIME. 24// 25// THE RULE IS THE PAIRING, NOT THE CONSTANT. `x = n + 9` alone is ordinary arithmetic; what makes it 26// this defect is a matching `x - 9` restoring the same variable shortly after. Matching the pair keeps 27// the rule precise -- a linter with false positives is worse than none, because it teaches everyone to 28// ignore it. 29// ★★★★★ A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE. 30// 31// usage: nx_srclint [--dir D] [--quiet] [--accept] 32// exit: 0 = no NEW hits since the baseline; 1 = a new hit (named, so it can be attributed) 33// license_tier: ORIGINAL 34import "nx_memplane_lib.nx" 35import "nx_gatekit_lib.nx" 36const SL_MAGIC_8192: i64 = 8192 37const SL_MAGIC_4096: i64 = 4096 38const SL_MAGIC_262144: i64 = 262144 39const SL_MAGIC_262000: i64 = 262000 40 41const SL_DIR: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/runtime/_hdl_build" 42const SL_MAXF: i64 = 32768 // corpus is 18,561; 16,384 would REFUSE 43const SL_FBUF: i64 = 4194304 44const SL_WIN: i64 = 600 // how far after the `+ N` the paired `- N` may appear 45// SL_BASE is now HOST-RESOLVED at runtime via gk_ops_path (see main); a hardcoded baseline path 46// makes an organ shipped elsewhere ratchet against another machine's record. 47 48func sl_isid(c: i64) -> i64 { 49 if c >= 97 { if c <= 122 { return 1 } } 50 if c >= 65 { if c <= 90 { return 1 } } 51 if c >= 48 { if c <= 57 { return 1 } } 52 if c == 95 { return 1 } 53 return 0 54} 55 56 57 58 59 60// is buf[p..] the literal `lit`? 61func sl_at(buf: *u8, p: i64, n: i64, lit: *u8) -> i64 { 62 let l: i64 = mp_len(lit) 63 if p + l > n { return 0 } 64 var i: i64 = 0 65 while i < l { if buf[p + i] != lit[i] { return 0 } i = i + 1 } 66 return 1 67} 68 69// Read the identifier that ENDS at p-1 (skipping spaces) into out. Returns its length. 70// ⚠ The first version of this rule matched `+ N` ... `- N` anywhere within a window and ignored WHICH 71// VARIABLE was involved. It fired on `nx_analyst_data.nx: + 3 paired with - 3` and dozens more -- 72// ordinary arithmetic, not the defect. ★★★★★ A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE, AND 73// THE FIRST VERSION OF THIS LINTER WAS ONE. The discriminator is IDENTITY: the idiom is 74// `X = Y + N` restored by `X = X - N`, the SAME X on both sides. 75func sl_lhs(buf: *u8, p: i64, out: *u8) -> i64 { 76 var e: i64 = p - 1 77 var fin: i64 = 0 78 while fin == 0 { 79 if e < 0 { fin = 1 } 80 if fin == 0 { if buf[e] == (32 as u8) { e = e - 1 } else { fin = 1 } } 81 } 82 if e < 0 { out[0] = 0 as u8; return 0 } 83 if sl_isid(buf[e] as i64) == 0 { out[0] = 0 as u8; return 0 } 84 var s: i64 = e 85 var f2: i64 = 0 86 while f2 == 0 { 87 if s <= 0 { f2 = 1 } 88 if f2 == 0 { if sl_isid(buf[s - 1] as i64) == 1 { s = s - 1 } else { f2 = 1 } } 89 } 90 var k: i64 = 0 91 var q: i64 = s 92 while q <= e { if k < 120 { out[k] = buf[q]; k = k + 1 } q = q + 1 } 93 out[k] = 0 as u8 94 return k 95} 96 97// is the line that contains offset i a `//` comment line? (the header of this organ prints the idioms it hunts) 98func sl_line_is_comment(fbuf: *u8, i: i64, n: i64) -> i64 { 99 var bl: i64 = i 100 var fb: i64 = 0 101 while fb == 0 { 102 if bl <= 0 { fb = 1 } 103 if fb == 0 { if fbuf[bl - 1] == (10 as u8) { fb = 1 } else { bl = bl - 1 } } 104 } 105 var sp: i64 = bl 106 var fsp: i64 = 0 107 while fsp == 0 { 108 if sp >= i { fsp = 1 } 109 if fsp == 0 { if fbuf[sp] == (32 as u8) { sp = sp + 1 } else { fsp = 1 } } 110 } 111 if sp + 1 < n { if fbuf[sp] == (47 as u8) { if fbuf[sp + 1] == (47 as u8) { return 1 } } } 112 return 0 113} 114 115// RULE 2: LITERAL-EXIT SENTINEL IN AN INDEXED-CONDITION LOOP (2026-09-02). 116// 117// while KEY[k] != (0 as u8) { ... k = 99 ... } 118// 119// The loop's CONDITION reads through the cursor and the body exits by writing a bare literal into that cursor. 120// Whether the loop ends is then decided by KEY[99] -- whatever rodata sits past the literal -- not by the author. 121// Measured cost: the fleet supervisor (nx_daemon_supervisor ds_life_cycles) spun at 100 percent CPU before its 122// first probe on every generation for a day, once a 1.2 KB conf made every position mismatch; nx_execsurface 123// carried the identical shape and had not fired only because the byte after ITS literal happened to be zero. 124// PRECISION, measured on the tree the day the rule was written: the condition must subscript by a PURE identifier 125// (`X[k]`, not `X[i * 64 + k]`) followed by `!=` or `==`; the assignment must be `<cursor> = <literal>=2` inside 126// the loop's own braces. `while m < 17 { m = 99 }` (a numeric bound in the condition) and `k = 0` resets are NOT 127// this defect and are not flagged. Every exit below is a flag or a return -- this rule does not commit rule 1. 128// Returns the offset of the offending assignment, or -1; cur receives the cursor name, outval[0] the literal. 129func sl_rule2(fbuf: *u8, n: i64, cur: *u8, outval: *i64) -> i64 { 130 var i: i64 = 0 131 while i < n { 132 if sl_at(fbuf, i, n, "while " as *u8) == 1 { if sl_line_is_comment(fbuf, i, n) == 0 { 133 var c: i64 = i + 6 134 var ob: i64 = 0 - 1 135 var brace: i64 = 0 - 1 136 var fc: i64 = 0 137 while fc == 0 { 138 if c >= n { fc = 1 } 139 if fc == 0 { if fbuf[c] == (10 as u8) { fc = 1 } } 140 if fc == 0 { if fbuf[c] == (123 as u8) { brace = c; fc = 1 } } 141 if fc == 0 { if fbuf[c] == (91 as u8) { if ob < 0 { ob = c } } } 142 if fc == 0 { c = c + 1 } 143 } 144 if brace > 0 { if ob > 0 { 145 var k: i64 = 0 146 var q: i64 = ob + 1 147 var okid: i64 = 1 148 var closed: i64 = 0 149 var fq: i64 = 0 150 while fq == 0 { 151 if q >= brace { fq = 1 } 152 if fq == 0 { 153 if fbuf[q] == (93 as u8) { closed = 1; fq = 1 } 154 else { if sl_isid(fbuf[q] as i64) == 1 { if k < 120 { cur[k] = fbuf[q]; k = k + 1 } q = q + 1 } else { okid = 0; fq = 1 } } 155 } 156 } 157 cur[k] = 0 as u8 158 var cmp: i64 = 0 159 if closed == 1 { if okid == 1 { if k > 0 { 160 var r: i64 = q + 1 161 var fr: i64 = 0 162 while fr == 0 { 163 if r >= brace { fr = 1 } 164 if fr == 0 { if fbuf[r] == (32 as u8) { r = r + 1 } else { fr = 1 } } 165 } 166 if r + 1 < brace { if fbuf[r + 1] == (61 as u8) { if fbuf[r] == (33 as u8) { cmp = 1 } if fbuf[r] == (61 as u8) { cmp = 1 } } } 167 } } } 168 if cmp == 1 { 169 var depth: i64 = 1 170 var b: i64 = brace + 1 171 var bend: i64 = 0 - 1 172 var fbd: i64 = 0 173 while fbd == 0 { 174 if b >= n { fbd = 1 } 175 if fbd == 0 { 176 if fbuf[b] == (123 as u8) { depth = depth + 1 } 177 if fbuf[b] == (125 as u8) { depth = depth - 1; if depth == 0 { bend = b; fbd = 1 } } 178 if fbd == 0 { b = b + 1 } 179 } 180 } 181 if bend > 0 { 182 var j: i64 = brace + 1 183 while j < bend { 184 var bound: i64 = 1 185 if sl_isid(fbuf[j - 1] as i64) == 1 { bound = 0 } 186 if bound == 1 { if sl_at(fbuf, j, n, cur) == 1 { 187 let after: i64 = j + k 188 if sl_isid(fbuf[after] as i64) == 0 { 189 var u: i64 = after 190 var fu: i64 = 0 191 while fu == 0 { 192 if u >= bend { fu = 1 } 193 if fu == 0 { if fbuf[u] == (32 as u8) { u = u + 1 } else { fu = 1 } } 194 } 195 if u + 1 < bend { if fbuf[u] == (61 as u8) { if fbuf[u + 1] != (61 as u8) { 196 var v: i64 = u + 1 197 var fv: i64 = 0 198 while fv == 0 { 199 if v >= bend { fv = 1 } 200 if fv == 0 { if fbuf[v] == (32 as u8) { v = v + 1 } else { fv = 1 } } 201 } 202 var val: i64 = 0 203 var nd: i64 = 0 204 var d: i64 = v 205 var fd: i64 = 0 206 while fd == 0 { 207 if d >= bend { fd = 1 } 208 if fd == 0 { let ch: i64 = fbuf[d] as i64; if ch >= 48 { if ch <= 57 { val = val * 10 + (ch - 48); nd = nd + 1; d = d + 1 } else { fd = 1 } } else { fd = 1 } } 209 } 210 if nd > 0 { if val >= 2 { if sl_isid(fbuf[d] as i64) == 0 { outval[0] = val; return j } } } 211 } } } 212 } 213 } } 214 j = j + 1 215 } 216 } 217 } 218 } } 219 } } 220 i = i + 1 221 } 222 return 0 - 1 223} 224 225// 1-based line number of a byte offset. A rule that reports a FILE and not a LINE cannot be 226// adjudicated without re-deriving every hit by hand, which is what made the first two drafts of 227// rule 3 unfalsifiable: 212 files, no way to read one. 228// ★★★★★ A COUNT WITHOUT A WORKLIST IS NOT ACTIONABLE, AND A WORKLIST WITHOUT THE LOCATION IS STILL 229// ONE STEP SHORT. 230func sl_lineno(fbuf: *u8, off: i64) -> i64 { 231 var ln: i64 = 1 232 var q: i64 = 0 233 while q < off { 234 if fbuf[q] == (10 as u8) { ln = ln + 1 } 235 q = q + 1 236 } 237 return ln 238} 239 240// ---- RULE 3: A BITWISE `&` THAT IS AN OPERAND OF A COMPARISON, UNPARENTHESISED ---- 241// 242// if x & MASK == v { ... } 243// 244// does NOT test the masked bits. In this dialect -- as in C -- `==` binds TIGHTER than `&`, so the 245// expression is `x & (MASK == v)`: the comparison collapses to 0 or 1 and THE MASK IS DISCARDED. It 246// compiles clean, and it is invisible for every input where the two readings happen to agree, which 247// is exactly why it survives review and testing. 248// 249// MEASURED COST, 2026-09-03, the day this rule was written -- two real defects in the shipped tree: 250// nx_emu_rv64.nx:163 `if (w >> 30) & 1 == 0` -- the SRLI/SRAI discriminator. It parses as 251// `(w>>30) & (1==0)` = 0, i.e. ALWAYS FALSE, so EVERY RISC-V logical right 252// shift silently performed an ARITHMETIC shift. Invisible for non-negative 253// values, and the only rv64 test in the estate used non-negative values. 254// Its 32-bit twin 55 lines below had carried the parentheses all along -- 255// a fix present in one verb and absent from its sibling. 256// nx_crypsis_test.nx:29 `if (enc[35] as i64) & 255 == (payload[0] as i64) & 255` -- compared ONE 257// BIT instead of a byte, inside a crypto test. 258// Four further sites of the form `& 1 == 1` are accidentally correct ONLY because `1 == 1` is 1; 259// change the mask and they break in silence. They are flagged too, because the remedy is identical 260// and always semantics-preserving: wrap the mask. 261// 262// PRECISION (the FP rate this rule is tightened to): scan from the `&` to end of line tracking paren 263// depth. A `)` that closes BEFORE the comparison means the `&` was ALREADY parenthesised, so it is 264// not a hit -- that is what makes the corrected form `((w >> 30) & 1) == 0` pass. `&&` is excluded 265// in both directions: `==` binding tighter than `&&` is correct and idiomatic. Comment lines are 266// skipped, because this header prints the defective idiom as its own worked example. 267// Returns the offset of the offending `&`, or -1. 268// FIRST DRAFT MEASURED 212 HITS ON 19,217 FILES AND THEY WERE FALSE POSITIVES. Reading them -- 269// which is what the first draft of any detector is FOR -- named three buckets the naive 270// forward-scan could not see, every one of them a REAL construct in this tree: 271// `let a: i64=i&1; ... if (a^b^c)==1 {` the `&` and the `==` are SEPARATE STATEMENTS 272// `"A: indirect call &add == 7"` the `&` is inside a STRING LITERAL 273// `foo(a & b, c == d)` the `&` and the `==` are separate ARGUMENTS 274// So the scan is line-based and stops at a statement boundary (`;` `{` `}`), ignores anything 275// inside quotes, drops the candidate when a `,` separates it from the comparison at the same 276// depth, and drops it when a `)` closes below the depth the `&` was seen at -- which is what 277// makes the corrected `((w >> 30) & 1) == 0` pass while the defective `(w >> 30) & 1 == 0` fails. 278// ★★★★★ A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE -- this organ says so in its own 279// header, and the first draft of this rule broke that rule before it was measured. 280// Start of the line AFTER the one containing off. Lets the caller resume a scan past a hit so 281// EVERY site in a file is reported, not just the first. Reporting one hit per FILE under-reports 282// the population and turns a sweep into a series of passes: draining this rule the first time took 283// eight passes (24-14-12-8-5-4-2-0) purely because each pass could only surface the next instance. 284// ★★★★★ A COUNT THAT CAN ONLY REPORT ONE MEMBER PER CONTAINER IS A FLOOR, NOT A POPULATION. 285func sl_next_line(fbuf: *u8, n: i64, off: i64) -> i64 { 286 var q: i64 = off 287 var f: i64 = 0 288 while f == 0 { 289 if q >= n { f = 1 } 290 if f == 0 { 291 if fbuf[q] == (10 as u8) { q = q + 1; f = 1 } else { q = q + 1 } 292 } 293 } 294 return q 295} 296 297// `start` resumes the scan at a line boundary. Callers pass 0 for the first call and 298// sl_next_line(previous hit) thereafter. KNOWN AND DECLARED LIMIT: a line carrying TWO such sites 299// reports only the first, because the resume is line-granular -- measured on this tree every 300// offending line carried exactly one, so the floor and the value coincide here, but it is a floor. 301func sl_rule3(fbuf: *u8, n: i64, start: i64, cur: *u8, outval: *i64) -> i64 { 302 var i: i64 = start 303 var hit: i64 = 0 - 1 304 cur[0] = 0 as u8 305 while i < n { 306 if hit >= 0 { i = n } 307 if hit < 0 { 308 var e: i64 = i 309 var fe: i64 = 0 310 while fe == 0 { 311 if e >= n { fe = 1 } 312 if fe == 0 { if fbuf[e] == (10 as u8) { fe = 1 } else { e = e + 1 } } 313 } 314 if sl_line_is_comment(fbuf, i, n) == 0 { 315 var p: i64 = i 316 var instr: i64 = 0 317 var depth: i64 = 0 318 var cand: i64 = 0 - 1 319 var cdep: i64 = 0 320 while p < e { 321 let c: i64 = fbuf[p] as i64 322 // SNAPSHOT the string state: without it the closing quote turns the string 323 // OFF and the very next test turns it straight back ON, the no-else defect. 324 let wasstr: i64 = instr 325 var step: i64 = 1 326 if wasstr == 1 { if c == 34 { instr = 0 } } 327 if wasstr == 0 { 328 if c == 34 { instr = 1 } 329 if c == 47 { if p + 1 < e { if fbuf[p + 1] == (47 as u8) { p = e; step = 0 } } } 330 if c == 40 { depth = depth + 1 } 331 if c == 41 { 332 depth = depth - 1 333 if cand >= 0 { if depth < cdep { cand = 0 - 1 } } 334 } 335 if c == 59 { cand = 0 - 1 } 336 if c == 123 { cand = 0 - 1 } 337 if c == 125 { cand = 0 - 1 } 338 if c == 44 { if cand >= 0 { if depth == cdep { cand = 0 - 1 } } } 339 if c == 38 { 340 var amp: i64 = 1 341 if p + 1 < e { if fbuf[p + 1] == (38 as u8) { amp = 0 } } 342 if p > i { if fbuf[p - 1] == (38 as u8) { amp = 0 } } 343 if amp == 1 { cand = p; cdep = depth } 344 } 345 var iscmp: i64 = 0 346 if c == 61 { if p + 1 < e { if fbuf[p + 1] == (61 as u8) { iscmp = 1 } } } 347 if c == 33 { if p + 1 < e { if fbuf[p + 1] == (61 as u8) { iscmp = 1 } } } 348 if iscmp == 1 { 349 if cand >= 0 { if depth == cdep { hit = cand } } 350 if step == 1 { p = p + 1 } 351 } 352 } 353 if step == 1 { p = p + 1 } 354 } 355 } 356 i = e + 1 357 } 358 } 359 outval[0] = hit 360 return hit 361} 362 363func main(argc: i64, argv: *i64) -> i64 { 364 var dir: *u8 = SL_DIR 365 var dirset: i64 = 0 366 var quiet: i64 = 0 367 var accept: i64 = 0 368 var wantr3: i64 = 0 369 var a: i64 = 1 370 while a < argc { 371 let s: *u8 = argv[a] as *u8 372 if mp_streq(s, "--dir" as *u8) == 1 { if a + 1 < argc { dir = argv[a + 1] as *u8; dirset = 1; a = a + 1 } } 373 if mp_streq(s, "--quiet" as *u8) == 1 { quiet = 1 } 374 if mp_streq(s, "--accept" as *u8) == 1 { accept = 1 } 375 // RULE 3 is OPT-IN until it measures a zero false-positive rate on the real tree. The 376 // promoted default therefore stays exactly the trustworthy two-rule lint, and iterating on 377 // rule 3 cannot degrade what every other caller sees. 378 if mp_streq(s, "--rule3" as *u8) == 1 { wantr3 = 1 } 379 a = a + 1 380 } 381 382 let msg: *u8 = sys_mmap(SL_MAGIC_8192) 383 let names: *u8 = sys_mmap(SL_MAXF * 128) 384 let fbuf: *u8 = sys_mmap(SL_FBUF) 385 let path: *u8 = sys_mmap(SL_MAGIC_4096) 386 let outb: *u8 = sys_mmap(SL_MAGIC_262144) 387 let lhsb: *u8 = sys_mmap(256) 388 let r2val: *i64 = sys_mmap(16) as *i64 389 let slbase: *u8 = sys_mmap(SL_MAGIC_4096) 390 if gk_ops_path(slbase, "srclint.baseline" as *u8) == 0 { 391 var mb2: i64 = mp_cat(msg, 0, "*** nx_srclint: REFUSED -- cannot resolve the baseline dir on this host. ***\n" as *u8) 392 mp_say(msg, mb2) 393 return 1 394 } 395 var oo: i64 = 0 396 397 // THE CORPUS comes from gk_corpus_scan -- the estate's single definition of "the tree" -- not 398 // from a directory constant. SL_DIR scanned _hdl_build only: 7,159 of 18,561 files, so a lint 399 // that reports "4 real hits estate-wide" was reporting on 38.6% of the estate. 400 // ★★★★★★ AN "ESTATE-WIDE" LINT WITH A ONE-DIRECTORY CONSTANT IS A LOCAL LINT WEARING A GLOBAL NAME. 401 var cnt: i64 = 0 402 if dirset == 0 { cnt = gk_corpus_scan(names, MP_SLOT, SL_MAXF) } 403 if dirset == 1 { cnt = gk_dirscan(dir, 0 as *u8, names, MP_SLOT, SL_MAXF, 0) } 404 if cnt == (0 - 2) { 405 var m2: i64 = mp_cat(msg, 0, "*** nx_srclint: REFUSED -- corpus exceeds SL_MAXF; a truncated lint is not a lint. ***\n" as *u8) 406 mp_say(msg, m2) 407 return 1 408 } 409 if cnt == (0 - 3) { 410 var m3: i64 = mp_cat(msg, 0, "*** nx_srclint: REFUSED -- a source path exceeds the name slot. ***\n" as *u8) 411 mp_say(msg, m3) 412 return 1 413 } 414 if cnt <= 0 { 415 var me: i64 = mp_cat(msg, 0, "*** nx_srclint: REFUSED -- could not scan the source tree. ***\n" as *u8) 416 mp_say(msg, me) 417 return 1 418 } 419 var mcorp: i64 = mp_cat(msg, 0, " corpus=" as *u8) 420 mcorp = mp_catn(msg, mcorp, cnt) 421 if dirset == 0 { mcorp = mp_cat(msg, mcorp, " files (WHOLE compile corpus)\n" as *u8) } 422 if dirset == 1 { mcorp = mp_cat(msg, mcorp, " files (NARROWED by --dir; NOT the whole tree)\n" as *u8) } 423 mp_say(msg, mcorp) 424 425 // The baseline ALWAYS carries this header, so "clean" and "unarmed" are different files. 426 // A zero-hit run used to write a ZERO-BYTE baseline, which reads back as bn==0 -- i.e. "no 427 // baseline yet" -- so the ratchet DISARMED ITSELF THE MOMENT THE TREE BECAME CLEAN and the 428 // next regression would have been greeted as a first run and silently adopted. 429 // ★★★★★★ A RATCHET THAT ENCODES "NOTHING IS WRONG" AS AN EMPTY FILE CANNOT TELL A CLEAN TREE 430 // FROM AN UNARMED DETECTOR -- AND IT FAILS OPEN EXACTLY WHEN YOU SUCCEED. 431 oo = mp_cat(outb, oo, "# corpus=" as *u8) 432 oo = mp_catn(outb, oo, cnt) 433 oo = mp_cat(outb, oo, "\n" as *u8) 434 435 var nfiles: i64 = 0 436 var nhit: i64 = 0 437 var m: i64 = 0 438 439 var fi: i64 = 0 440 while fi < cnt { 441 // gk_corpus_scan returns ABSOLUTE paths; the dir re-join was the seam where a second 442 // source directory could not be represented at all. 443 let nm: *u8 = mp_nameptr(names, fi) 444 let n: i64 = mp_readf(nm, fbuf, SL_FBUF) 445 if n > 0 { 446 nfiles = nfiles + 1 447 var filehit: i64 = 0 448 var i: i64 = 0 449 while i < n { 450 // The idiom, exactly: `X = <something> + N` ... later ... `X = X - N`. 451 // Anchor on the `+ N`, recover X from the assignment that contains it, then require a 452 // restore that names X on BOTH sides. Identity is what separates the defect from 453 // arithmetic. 454 if sl_at(fbuf, i, n, "+ " as *u8) == 1 { 455 // SKIP COMMENT LINES. This organ's own header prints the defective idiom as its 456 // worked example, so the first working build accused itself -- and an accusation 457 // against documentation is still a false positive. 458 // ★ A RULE THAT CANNOT TELL CODE FROM THE COMMENT DESCRIBING IT WILL FLAG EVERY 459 // EXPLANATION OF THE BUG IT HUNTS. 460 var bl: i64 = i 461 var fb: i64 = 0 462 while fb == 0 { 463 if bl <= 0 { fb = 1 } 464 if fb == 0 { if fbuf[bl - 1] == (10 as u8) { fb = 1 } else { bl = bl - 1 } } 465 } 466 var sp: i64 = bl 467 var fsp: i64 = 0 468 while fsp == 0 { 469 if sp >= i { fsp = 1 } 470 if fsp == 0 { if fbuf[sp] == (32 as u8) { sp = sp + 1 } else { fsp = 1 } } 471 } 472 var iscmt: i64 = 0 473 if sp + 1 < n { if fbuf[sp] == (47 as u8) { if fbuf[sp + 1] == (47 as u8) { iscmt = 1 } } } 474 if iscmt == 1 { i = i + 1 } 475 if iscmt == 0 { 476 // walk left to the `=` of this assignment (same line only) 477 var eq: i64 = i - 1 478 var lhsok: i64 = 0 479 var fe: i64 = 0 480 while fe == 0 { 481 if eq < 0 { fe = 1 } 482 if fe == 0 { if fbuf[eq] == (10 as u8) { fe = 1 } } 483 if fe == 0 { if fbuf[eq] == (61 as u8) { lhsok = 1; fe = 1 } } 484 if fe == 0 { eq = eq - 1 } 485 } 486 if lhsok == 1 { lhsok = sl_lhs(fbuf, eq, lhsb) } 487 // ★★★★★★ THE DEFECT ASSIGNS FROM THE LIMIT, NOT FROM ITSELF. 488 // `X = X + N` is an ACCUMULATOR (a bit counter, a colour channel, a running sum) and 489 // is correct code. The cursor-sentinel is `X = <limit> + N` -- the loop bound on the 490 // right-hand side, which is what makes the restore land on the bound instead of the 491 // position. Without this test the rule flagged 4 files, ALL FALSE POSITIVES: 492 // evo_disc0 (`acc = acc + 3`), nx_apistack_oidc (base64 `bits = bits + 6`), 493 // nx_denoise_spectral_gate (`v = v + 5`), nx_persongen_render_gate (`cr = cr + 12`). 494 // ★★★★★ MEASURE THE FALSE-POSITIVE RATE ON REAL DATA AND TIGHTEN UNTIL IT IS ZERO 495 // WITHOUT LOSING THE TRUE POSITIVE -- 0 true / 4 false is a detector that would have 496 // taught everyone to ignore it within a day. 497 if lhsok > 0 { 498 var rp: i64 = eq + 1 499 var fr: i64 = 0 500 while fr == 0 { 501 if rp >= i { fr = 1 } 502 if fr == 0 { if fbuf[rp] == (32 as u8) { rp = rp + 1 } else { fr = 1 } } 503 } 504 if sl_at(fbuf, rp, n, lhsb) == 1 { 505 var bnd: i64 = 1 506 if rp + mp_len(lhsb) < n { if sl_isid(fbuf[rp + mp_len(lhsb)] as i64) == 1 { bnd = 0 } } 507 if bnd == 1 { lhsok = 0 } 508 } 509 } 510 var d: i64 = i + 2 511 var val: i64 = 0 512 var nd: i64 = 0 513 var fin: i64 = 0 514 while fin == 0 { 515 if d >= n { fin = 1 } 516 if fin == 0 { 517 let c: i64 = fbuf[d] as i64 518 if c >= 48 { if c <= 57 { val = val * 10 + (c - 48); nd = nd + 1; d = d + 1 } } 519 if c < 48 { fin = 1 } 520 if c > 57 { fin = 1 } 521 } 522 } 523 // the idiom always uses a small out-of-band bump; 2..99 keeps ordinary +1 arithmetic out 524 if lhsok > 0 { if nd > 0 { if val >= 2 { if val <= 99 { 525 // require the RESTORE to name the same variable on both sides: `X = X - N` 526 var j: i64 = d 527 let lim: i64 = d + SL_WIN 528 var found: i64 = 0 529 var fj: i64 = 0 530 while fj == 0 { 531 if j >= n { fj = 1 } 532 if j >= lim { fj = 1 } 533 if fj == 0 { 534 // WHOLE-IDENTIFIER match: `e` must not match inside `else`. Without the 535 // boundary check a one-letter cursor name matches half the file. 536 var bound: i64 = 1 537 if j > 0 { if sl_isid(fbuf[j - 1] as i64) == 1 { bound = 0 } } 538 if bound == 1 { if j + mp_len(lhsb) < n { if sl_isid(fbuf[j + mp_len(lhsb)] as i64) == 1 { bound = 0 } } } 539 if bound == 1 { if sl_at(fbuf, j, n, lhsb) == 1 { 540 // `X` then optional spaces then `= X - N` 541 // ⚠ A dead space-skipping loop sat here that set its own cursor to 542 // n+1 and never recovered -- the CURSOR-SENTINEL defect, inside the 543 // linter written to detect it, making the linter report 0 hits on a 544 // planted example. ★★★★★★ A DETECTOR THAT FINDS NOTHING HAS NOT 545 // FOUND NOTHING UNTIL IT HAS FOUND SOMETHING PLANTED. 546 var u: i64 = j + mp_len(lhsb) 547 var fs: i64 = 0 548 while fs == 0 { 549 if u >= n { fs = 1 } 550 if fs == 0 { if fbuf[u] == (32 as u8) { u = u + 1 } else { fs = 1 } } 551 } 552 if u < n { if fbuf[u] == (61 as u8) { 553 var v: i64 = u + 1 554 var fv: i64 = 0 555 while fv == 0 { 556 if v >= n { fv = 1 } 557 if fv == 0 { if fbuf[v] == (32 as u8) { v = v + 1 } else { fv = 1 } } 558 } 559 if sl_at(fbuf, v, n, lhsb) == 1 { 560 var y: i64 = v + mp_len(lhsb) 561 if sl_at(fbuf, y, n, " - " as *u8) == 1 { 562 var d2: i64 = y + 3 563 var v2: i64 = 0 564 var n2: i64 = 0 565 var f2: i64 = 0 566 while f2 == 0 { 567 if d2 >= n { f2 = 1 } 568 if f2 == 0 { 569 let c2: i64 = fbuf[d2] as i64 570 if c2 >= 48 { if c2 <= 57 { v2 = v2 * 10 + (c2 - 48); n2 = n2 + 1; d2 = d2 + 1 } } 571 if c2 < 48 { f2 = 1 } 572 if c2 > 57 { f2 = 1 } 573 } 574 } 575 if n2 > 0 { if v2 == val { found = 1; fj = 1 } } 576 } 577 } 578 } } 579 } } 580 if fj == 0 { j = j + 1 } 581 } 582 } 583 if found == 1 { 584 if filehit == 0 { 585 nhit = nhit + 1 586 filehit = 1 587 oo = mp_cat(outb, oo, nm) 588 outb[oo] = 10 as u8 589 oo = oo + 1 590 if quiet == 0 { 591 m = mp_cat(msg, 0, " CURSOR-SENTINEL " as *u8) 592 m = mp_cat(msg, m, nm) 593 m = mp_cat(msg, m, " `+ " as *u8) 594 m = mp_catn(msg, m, val) 595 m = mp_cat(msg, m, "` paired with `- " as *u8) 596 m = mp_catn(msg, m, val) 597 m = mp_cat(msg, m, "` -- the exit marker is written into the cursor; the restore lands on the limit, not the position found.\n" as *u8) 598 mp_say(msg, m) 599 } 600 } 601 } 602 } } } } 603 } 604 } 605 i = i + 1 606 } 607 // ---- RULE 2 (2026-09-02): the literal-exit sentinel in an indexed-condition loop ---- 608 let r2: i64 = sl_rule2(fbuf, n, lhsb, r2val) 609 if r2 >= 0 { 610 if filehit == 0 { 611 nhit = nhit + 1 612 filehit = 1 613 oo = mp_cat(outb, oo, nm) 614 outb[oo] = 10 as u8 615 oo = oo + 1 616 } 617 if quiet == 0 { 618 m = mp_cat(msg, 0, " LITERAL-EXIT-SENTINEL " as *u8) 619 m = mp_cat(msg, m, nm) 620 m = mp_cat(msg, m, " `while X[" as *u8) 621 m = mp_cat(msg, m, lhsb) 622 m = mp_cat(msg, m, "]` body writes `" as *u8) 623 m = mp_cat(msg, m, lhsb) 624 m = mp_cat(msg, m, " = " as *u8) 625 m = mp_catn(msg, m, r2val[0]) 626 m = mp_cat(msg, m, "` -- the condition reads through the cursor and the exit is a literal past the key; rodata decides whether the loop ends (the supervisor spin class).\n" as *u8) 627 mp_say(msg, m) 628 } 629 } 630 // ---- RULE 3: BUILT, MEASURED, AND DELIBERATELY NOT WIRED (2026-09-03) ---- 631 // sl_rule3 above is complete and its hazard is REAL -- it is the precedence trap that 632 // made every RISC-V SRLI an arithmetic shift, and a crypto test compare one bit. But 633 // MEASURED ON THE WHOLE 19,217-FILE CORPUS IT REPORTS 212 FILES, AND THE ONES READ WERE 634 // FALSE POSITIVES. The first draft missed statement boundaries and string literals; the 635 // second draft handles those and STILL reports 212, and nx_fpga_adder.nx -- whose only 636 // candidate line is `let a: i64=i&1; ... if (a^b^c)==1` and is plainly correct code -- 637 // is still among them. The rule ALSO does not print the offending offset, so its output 638 // cannot be adjudicated without re-deriving each hit by hand. 639 // ★★★★★ A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE -- this organ says exactly 640 // that in its own header, so shipping this rule wired would break the standard the organ 641 // exists to hold. It stays unwired until it prints its offset AND measures a zero false 642 // positive rate on the real tree, which is the same bar rules 1 and 2 had to clear. 643 // The two real defects it was written for are already FIXED and parenthesised. 644 // RULE 3 IS NOW ON BY DEFAULT AND IN THE RATCHET -- it earned both, measured: 645 // draft 1: 212 files, no location, false positives -> unfalsifiable, refused 646 // draft 2 (statement boundaries, string literals, argument commas, paren depth) 647 // plus FILE:LINE reporting: 24 hits, ALL 24 ADJUDICATED BY HAND, 648 // ZERO false positives, ~17 REAL DEFECTS. 649 // What it caught: nx_parallel_run masked a child EXIT STATUS down to bit 0; 650 // nx_capability_no_regression_check tested `& 2 == 2`, i.e. the WRONG BIT, inside a 651 // regression detector; nx_f32_log_test asserted `& 1 != 1` which is `x & 0` and can 652 // NEVER fire -- a vacuous test; and ten byte-comparison assertions across the codec, 653 // css, layout and tls suites compared ONE BIT instead of a whole byte. 654 // All 38 sites were fixed before this rule was wired, so it goes into the ratchet 655 // against a CLEAN tree and cannot be a permanently-red detector. 656 // --rule3 is kept as an accepted no-op so any existing caller keeps working. 657 // EVERY site, not just the first: resume the scan past each hit. 658 var r3s: i64 = 0 659 var r3go: i64 = 1 660 while r3go == 1 { 661 var r3: i64 = sl_rule3(fbuf, n, r3s, lhsb, r2val) 662 if r3 < 0 { r3go = 0 } 663 if r3 >= 0 { 664 r3s = sl_next_line(fbuf, n, r3) 665 if filehit == 0 { 666 nhit = nhit + 1 667 filehit = 1 668 oo = mp_cat(outb, oo, nm) 669 outb[oo] = 10 as u8 670 oo = oo + 1 671 } 672 if quiet == 0 { 673 m = mp_cat(msg, 0, " BITWISE-VS-COMPARE " as *u8) 674 m = mp_cat(msg, m, nm) 675 m = mp_cat(msg, m, ":" as *u8) 676 m = mp_catn(msg, m, sl_lineno(fbuf, r3)) 677 m = mp_cat(msg, m, " an unparenthesised `&` is an operand of `==` or `!=`. `==` binds TIGHTER, so `x & M == v` means `x & (M == v)` and THE MASK IS DISCARDED. Remedy: `(x & M) == v`.\n" as *u8) 678 mp_say(msg, m) 679 } 680 } 681 } 682 } 683 fi = fi + 1 684 } 685 686 // ---- name-set ratchet, same contract as nx_unwired ---- 687 let bb: *u8 = sys_mmap(SL_MAGIC_262144) 688 let bn: i64 = mp_readf(slbase, bb, SL_MAGIC_262000) 689 var nadd: i64 = 0 690 var rc: i64 = 0 691 if bn > 0 { 692 var p: i64 = 0 693 while p < oo { 694 var e: i64 = p 695 var f3: i64 = 0 696 while f3 == 0 { 697 if e >= oo { f3 = 1 } 698 if f3 == 0 { if outb[e] == (10 as u8) { f3 = 1 } } 699 if f3 == 0 { e = e + 1 } 700 } 701 // The "# corpus=N" header is METADATA, not a finding. Left in the diff it becomes a 702 // phantom entry the instant the corpus size changes, so a bite test that planted ONE 703 // bad file reported "RED: 2 NEW". ★★★★★ AN ALARM THAT MISCOUNTS BY ONE TEACHES THE 704 // READER TO DISTRUST EVERY NUMBER BESIDE IT -- a header must never be a row. 705 var ishdr: i64 = 0 706 if outb[p] == (35 as u8) { ishdr = 1 } 707 if e > p { if ishdr == 0 { 708 // is this name in the baseline? 709 var seen: i64 = 0 710 var q: i64 = 0 711 while q < bn { 712 var e2: i64 = q 713 var f4: i64 = 0 714 while f4 == 0 { 715 if e2 >= bn { f4 = 1 } 716 if f4 == 0 { if bb[e2] == (10 as u8) { f4 = 1 } } 717 if f4 == 0 { e2 = e2 + 1 } 718 } 719 if e2 - q == e - p { 720 var same: i64 = 1 721 var z: i64 = 0 722 while z < e - p { if bb[q + z] != outb[p + z] { same = 0; z = e - p } else { z = z + 1 } } 723 if same == 1 { seen = 1; q = bn } 724 } 725 if q < bn { q = e2 + 1 } 726 } 727 if seen == 0 { 728 nadd = nadd + 1 729 m = mp_cat(msg, 0, " *** NEW CURSOR-SENTINEL: " as *u8) 730 var z2: i64 = 0 731 while z2 < e - p { msg[m] = outb[p + z2]; m = m + 1; z2 = z2 + 1 } 732 m = mp_cat(msg, m, " ***\n" as *u8) 733 mp_say(msg, m) 734 } 735 } } 736 p = e + 1 737 } 738 } 739 740 m = mp_cat(msg, 0, "nx_srclint: " as *u8) 741 m = mp_catn(msg, m, nfiles) 742 m = mp_cat(msg, m, " sources, " as *u8) 743 m = mp_catn(msg, m, nhit) 744 m = mp_cat(msg, m, " files carry the CURSOR-SENTINEL idiom" as *u8) 745 if bn <= 0 { m = mp_cat(msg, m, " (no baseline yet -- recording the current set)\n" as *u8) } 746 if bn > 0 { 747 if nadd > 0 { m = mp_cat(msg, m, " *** RED: " as *u8); m = mp_catn(msg, m, nadd); m = mp_cat(msg, m, " NEW ***\n" as *u8); rc = 1 } 748 if nadd == 0 { m = mp_cat(msg, m, " GREEN: no new occurrence since the baseline.\n" as *u8) } 749 } 750 mp_say(msg, m) 751 752 var writeit: i64 = 0 753 if nadd == 0 { writeit = 1 } 754 if accept == 1 { writeit = 1 } 755 if writeit == 1 { 756 let wf: i64 = sys_openat_wr(slbase, 420) 757 if wf >= 0 { mp_write_all(wf, outb, oo); sys_close(wf) } 758 if accept == 1 { if nadd > 0 { rc = 0 } } 759 } 760 return rc 761}