Record list-set mutation guidance
ober
87a536b0485db7d13462168190bc012bc6ee7e59
--- a/data/anti-patterns.sexp +++ b/data/anti-patterns.sexp @@ -4632,4 +4632,21 @@ "verify" "line_edit" "replace_range" - "jerboa_function_signature"))) + "jerboa_function_signature")) + (("advice" + . + "Use a mutable vector for fixed-position state and update fields with vector-set!, or keep lists immutable and rebuild/replace the parent value. For 2-D boards, use a vector of row vectors and vector-set! on the row vector. After a list-set! rejection on a missing-file draft, rewrite the state representation and resubmit corrected file contents rather than inspecting dependencies.") + ("avoid" + . + "Do not represent mutable record-like game/application state as a list and mutate fields with list-set!. Jerboa does not provide list-set!, and local models often then lose a whole first draft to syntax-guard rejection.") + ("id" . "invented-list-set-mutation") + ("kinds" "script" "debug-error" "test") + ("pattern" + . + "list-set!|list-set! is not available in Jerboa") + ("severity" . "medium") + ("tags" "list-set" "vector-set" "mutation" "state" + "local-model" "repair") + ("title" . "Do Not Use Invented list-set! For Jerboa State") + ("tools" "verify" "edit" "write" "replace_range" + "jerboa_error_fix_lookup"))) --- a/data/error-fixes.sexp +++ b/data/error-fixes.sexp @@ -2860,4 +2860,18 @@ ("pattern" . "variable atom-(set!|val) is not bound|Exception: variable atom-(set!|val) is not bound") + ("type" . "module-api")) + (("code_example" + . + ";; Wrong\n(def st (list 0 0 #f))\n(list-set! st 2 #t)\n\n;; Right for fixed-position mutable state\n(def st (vector 0 0 #f))\n(vector-set! st 2 #t)\n(vector-ref st 2)") + ("explanation" + . + "Jerboa exposes vector mutation primitives, but no list-set! helper. Generated game code often treats lists as mutable records; that fails before verification or as an unbound variable.") + ("fix" + . + "Do not use list-set!. For fixed-position mutable state use a vector and vector-set!/vector-ref; for lists, rebuild the list and replace the parent binding/value. For boards, use a vector of row vectors and vector-set! the row cell.") + ("id" . "jerboa-list-set-unavailable") + ("pattern" + . + "list-set! is not available in Jerboa|variable list-set! is not bound|Exception: variable list-set! is not bound") ("type" . "module-api"))) --- a/tests/test-model-guidance-data.ss +++ b/tests/test-model-guidance-data.ss @@ -44,8 +44,10 @@ (let* ([fixes (read-one "data/error-fixes.sexp")] [fix (entry-by-id fixes "jerboa-atom-accessor-unbound")] + [list-set-fix (entry-by-id fixes "jerboa-list-set-unavailable")] [anti-patterns (read-one "data/anti-patterns.sexp")] - [anti (entry-by-id anti-patterns "invented-jerboa-atom-accessors")]) + [anti (entry-by-id anti-patterns "invented-jerboa-atom-accessors")] + [list-set-anti (entry-by-id anti-patterns "invented-list-set-mutation")]) (check "atom unbound error fix exists" (and fix (string-contains? (entry-ref fix "pattern") "atom-") @@ -55,7 +57,17 @@ (and anti (string-contains? (entry-ref anti "avoid") "atom-set!") (string-contains? (entry-ref anti "avoid") "atom-val") - (string-contains? (entry-ref anti "advice") "verify immediately")))) + (string-contains? (entry-ref anti "advice") "verify immediately"))) + (check "list-set unavailable error fix exists" + (and list-set-fix + (string-contains? (entry-ref list-set-fix "pattern") "list-set!") + (string-contains? (entry-ref list-set-fix "fix") "vector-set!") + (string-contains? (entry-ref list-set-fix "fix") "rebuild"))) + (check "invented list-set anti-pattern exists" + (and list-set-anti + (string-contains? (entry-ref list-set-anti "avoid") "list-set!") + (string-contains? (entry-ref list-set-anti "advice") "vector-set!") + (string-contains? (entry-ref list-set-anti "advice") "missing-file draft")))) (if (= fail 0) (begin