code wiki / _hdl_build / nx_lang_lint_ext.nx
nx_lang_lint_ext.nx
buildroot/runtime/_hdl_build/nx_lang_lint_ext.nx
about
nx_lang_lint_ext.nx -- TWO landmine classes nx_lang_lint does not yet see, both paid for in build cycles
on 2026-07-25/26. This EXTENDS the existing linter's vocabulary; it does not replace it, and it
deliberately re-implements none of L1-L4.
L5 EMPTYESC -- an EMPTY LITERAL IN DISGUISE. L2 catches a literal "" by looking for two adjacent quote
bytes. It does not catch "\x00", which is four source characters and still a zero-length string, so it
hits the SAME nx_cc defect (a zero-length literal returns a pointer to the NEXT literal in the pool).
That is precisely the shape that segfaulted a gate: a sentinel spelled "\x00" silently became some
other string. A detector that catches only the obvious spelling of a defect leaves the subtle spelling
free to keep landing.
L6 UNDEFCONST -- an ALL-CAPS identifier that is USED but never DECLARED anywhere in the file or in what
it imports. nx_cc compiles these silently (seq1012, sev 9) and resolves them to whatever is to hand:
first a wild pointer, then -- after unrelated code moved the frame -- the caller's output buffer, so
the program ran and wrote WRONG DATA. A typo that does not fail the build is worse than one that does.
HONEST SCOPE, stated rather than implied: L6 covers CONSTANT-STYLE names only ([A-Z][A-Z0-9_]+). A
mistyped lowercase function or variable is NOT covered yet. That is a real limit, and it is written here
so nobody reads a clean run as more assurance than it is. Constant-style is where the failure actually
happened, and it is the subset that can be detected without resolving the whole language.
Declarations are gathered from the target AND its transitive imports, because an identifier arriving from
an import is not undefined -- a checker that cannot see through imports reports noise, and a checker that
reports noise gets switched off.
license_tier: ORIGINAL expect_exit: 0
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 28 | const LX_SRC: i64 = 2097152 |
| 29 | const LX_MAXDECL: i64 = 4096 |
| 30 | const LX_MAXIMP: i64 = 24 |
| 31 | const LX_NAME: i64 = 64 |
| 32 | const LX_ROOTS: i64 = 3 |
| 33 | const LX_PATH: i64 = 512 |
functions
| 35 | func lx_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 36 | func lx_n(v: i64) -> i64 { |
| 46 | func lx_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
called by 1: lx_kw_at |
| 47 | func lx_eq(a: *u8, b: *u8) -> i64 { |
| 56 | func lx_root(i: i64) -> *u8 {
called by 1: lx_read_any |
| 61 | func lx_read(path: *u8, buf: *u8, cap: i64) -> i64 { |
| 74 | func lx_read_any(name: *u8, buf: *u8, cap: i64, scratch: *u8) -> i64 { |
| 90 | func lx_is_upper(c: i64) -> i64 { if c>=65 { if c<=90 { return 1 } } return 0 }
called by 1: main |
| 91 | func lx_is_idchar(c: i64) -> i64 { |
| 99 | func lx_kw_at(b: *u8, n: i64, i: i64, word: *u8) -> i64 { |
| 108 | func lx_add_decl(names: *u8, cnt: *i64, nm: *u8) -> i64 { |
| 124 | func lx_take_ident(b: *u8, n: i64, i: i64, out: *u8) -> i64 { |
| 137 | func lx_harvest(b: *u8, n: i64, names: *u8, cnt: *i64) -> i64 { |
| 180 | func lx_imports(b: *u8, n: i64, imps: *u8, icnt: *i64) -> i64 { |
| 206 | func main(argc: i64, argv: *i64) -> i64 { |