9f3349b6716cc3ec8e657a194b9e4a8a9f463505
expansion terminated the whole process instead of reporting cleanly Found while verifying documentation for #[name] vs $[%name] (writing the new tag-hierarchy section surfaced that my own published example was factually wrong about which sigil reads a keyword attribute - #[name] actually reads local->variables, a separate store; $[%name] is the real keyword sigil, confirmed against Tag::getVariable directly). Referencing an uninitialized #[name] from inside a custom tag's own template crashed the process outright (SIGABRT) rather than reporting a normal error - confirmed via a real gdb backtrace, not guessed: 1. Tag::getVariable's "#[" branch and Tag::assignVariable's "local"/ "parent" scope branches all dereferenced `local` unconditionally. `local` is genuinely NULL at the true top level (jet-2.0.cpp's own `new jet::__jet(data, out, global, NULL, NULL)`) and stays NULL through any tag that doesn't establish its own local scope (most don't - <if>/<while>/<until> are the exception, passing `this` as local via their splitTagName constructor path). Added NULL checks with clear messages instead of an unconditional dereference. 2. The real root cause of the actual crash observed, though: jet-2.0.cpp's own top-level catch(coreutils::Exception) handler unconditionally calls data.setCursor(global.errorCursor) to show precise source-line context - but global.errorCursor is updated by every Tag's own construction, so it can (and, for an error originating inside a custom tag's own expanded template - container3 in __dotag.cpp - always does) point into a completely different ZString buffer than `data` (the top-level script). ZString::setCursor() correctly rejects a cursor outside its own buffer by throwing - but that throw happens while the ORIGINAL exception is still being handled inside this same catch block, and a second exception escaping an active catch block calls std::terminate() immediately, regardless of any enclosing try/catch. Confirmed via gdb: the abort's own stack showed __cxa_throw called from ZString::setCursor, called from the catch block itself. Fixed by wrapping the precise-location diagnostics in their own try/catch, falling back to just the error text (already printed first) plus a note that precise context isn't available, rather than crashing the whole process. The error text is the useful part either way - the line-context is a bonus only available when the cursor genuinely belongs to the top-level buffer. Verified via a full regression pass (9 real jet-2.0 runs covering custom tag scoping at 1/2/3 nesting levels, the dump tag tree, <try>/<catch>, <if>/<else>, and the corrected $[%name] keyword sigil) - all still work, plus the specific crash reproduction now reports a clean error instead of aborting.
Languages
HTML
50.2%
TeX
18.7%
C++
15%
JavaScript
7.5%
CSS
4.4%
Other
4.2%