Guide unbound canvas repair targets
Jaime Fournier <jaimef@linbsd.org>
e71c4a7e04f97dd6b18615ad7fc486d8bfd8af93
diff --git a/src/jcode/core/verified-run.ss b/src/jcode/core/verified-run.ss
index 74b6d74..8c1141b 100644
--- a/src/jcode/core/verified-run.ss
+++ b/src/jcode/core/verified-run.ss
@@ -1783,6 +1783,35 @@
(runtime-primitive-specific-guidance
detail primitive)))))))))
+(def (unbound-symbol-specific-guidance symbol)
+ (cond
+ ((string=? symbol "canvas")
+ (string-append
+ "\nQt callback scope hint: a top-level helper cannot see a `canvas` "
+ "bound inside `with-qt-app` or `let*`. Prefer the smallest repair that "
+ "keeps callbacks and repaint requests in scope: define the key/timer "
+ "handlers inside the same `let*` that binds `canvas`, pass `canvas` as "
+ "an argument to the helper, or initialize a top-level `current-canvas` "
+ "before callbacks can run."))
+ (else "")))
+
+(def (unbound-symbol-source-guidance detail cwd)
+ (let* ((symbol (unbound-variable-name detail))
+ (path (and symbol (failure-source-target-path detail cwd))))
+ (and path
+ (let ((absolute (abs-path cwd path)))
+ (and (file-exists? absolute)
+ (let* ((lines (string-split (read-file-string absolute) #\newline))
+ (targets
+ (source-target-lines
+ lines path (string-downcase symbol) 10)))
+ (and (pair? targets)
+ (string-append
+ "Source targets for unbound `" symbol "`:\n"
+ (string-join targets "\n")
+ "\nRepair the binding scope or the referenced name with the smallest local edit, then call verify."
+ (unbound-symbol-specific-guidance symbol)))))))))
+
(def (append-guidance-section base title text)
(if (and (string? text)
(> (string-length (string-trim text)) 0))
@@ -2020,14 +2049,21 @@
(source-guidance (runtime-source-guidance base cwd))
(with-source (append-guidance-section
repair-first "Source target guidance" source-guidance))
+ (unbound-source-guidance (unbound-symbol-source-guidance base cwd))
+ (with-unbound-source (append-guidance-section
+ with-source
+ "Unbound symbol source guidance"
+ unbound-source-guidance))
;; Keep one bounded automatic advisor. Error-fix and anti-pattern MCP
;; tools remain available for targeted model-directed lookup.
(mcp-advice
(bounded-failure-guidance (mcp-failure-guidance base cwd)))
(with-failure (if (usable-mcp-guidance? mcp-advice)
(append-guidance-section
- with-source "MCP failure advisor" mcp-advice)
- with-source)))
+ with-unbound-source
+ "MCP failure advisor"
+ mcp-advice)
+ with-unbound-source)))
with-failure))
(def failure-evidence-line-limit 12)
diff --git a/test/run.ss b/test/run.ss
index c83097e..aa91524 100644
--- a/test/run.ss
+++ b/test/run.ss
@@ -6506,6 +6506,31 @@
(str-contains? s "Do not rewrite the whole constant block")))))
(safe-delete-test-file! target-path))
+(let* ([vr-dir "/tmp"]
+ [target "jcode-verified-unbound-canvas-source-target.ss"]
+ [target-path (string-append vr-dir "/" target)]
+ [source "(import (jerboa prelude))\n(import (jerboa-qt qt))\n(def (handle-key!)\n (qt-paint-widget-update! canvas))\n(with-qt-app app\n (let* ([canvas (qt-paint-widget-create)])\n (qt-on-key-press! canvas (lambda () (handle-key!)))))\n"])
+ (safe-delete-test-file! target-path)
+ (write-test-output-file target-path
+ (lambda (o) (display source o))
+ 'replace)
+ (let* ([scope (parse-write-scope target)]
+ [result (parameterize ((current-write-scope scope))
+ (run-verify-command
+ "sh -c 'echo \"Exception: variable canvas is not bound\"; exit 1'"
+ vr-dir))])
+ (check! "verified-run: unbound canvas source guidance keeps verify failing"
+ (car result) #f)
+ (check-pred! "verified-run: unbound canvas guidance points at callback scope"
+ (cdr result)
+ (lambda (s)
+ (and (str-contains? s "Source targets for unbound `canvas`")
+ (str-contains? s "qt-paint-widget-update! canvas")
+ (str-contains? s "Qt callback scope hint")
+ (str-contains? s "current-canvas")
+ (str-contains? s "same `let*` that binds `canvas`")))))
+ (safe-delete-test-file! target-path))
+
(let* ([wf (coding-workflow "true" "/tmp"
(list (cons 'task-guidance "recipe-context-marker")))])
(check-pred! "verified-run: caller guidance appears in prompt"