-
-
Notifications
You must be signed in to change notification settings - Fork 0
fix(res-to-affine): report the effective engine and fail on degraded output #730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,18 +35,25 @@ let engine_label = function | |
| | Scanner_engine -> "scanner" | ||
| | Walker_engine -> "walker" | ||
|
|
||
| let run engine grammar_dir do_translate do_partial input output_opt = | ||
| let run engine grammar_dir do_translate do_partial allow_fallback input | ||
| output_opt = | ||
| if not (Sys.file_exists input) then begin | ||
| Format.eprintf "res-to-affine: input not found: %s@." input; | ||
| exit 2 | ||
| end; | ||
| (* Records whether the walker was ASKED for but could not run. Without this | ||
| the summary line reported the REQUESTED engine, so a fallback printed | ||
| "[walker]" while the scanner had produced the output -- which is why | ||
| whole-repo sweeps degraded silently and nobody noticed. *) | ||
| let degraded = ref false in | ||
| let source = read_file input in | ||
| let findings = | ||
| match engine with | ||
| | Scanner_engine -> Scanner.scan source | ||
| | Walker_engine -> | ||
| (try Walker.scan ~grammar_dir ~path:input ~source with | ||
| | Failure msg -> | ||
| degraded := true; | ||
| Format.eprintf "res-to-affine: %s@." msg; | ||
| Format.eprintf | ||
| "res-to-affine: falling back to scanner engine for %s@." | ||
|
|
@@ -62,6 +69,10 @@ let run engine grammar_dir do_translate do_partial input output_opt = | |
| else | ||
| match engine with | ||
| | Scanner_engine -> | ||
| (* Degraded even when the scanner was chosen deliberately: asking for | ||
| --translate/--partial and getting no translation is a useless | ||
| output whatever the intent, and a sweep must be able to see it. *) | ||
| degraded := true; | ||
| Format.eprintf | ||
| "res-to-affine: --translate/--partial need the walker engine; \ | ||
| no translation emitted for %s@." input; | ||
|
|
@@ -72,6 +83,7 @@ let run engine grammar_dir do_translate do_partial input output_opt = | |
| in | ||
| (try f ~grammar_dir ~path:input ~source with | ||
| | Failure msg -> | ||
| degraded := true; | ||
| Format.eprintf "res-to-affine: %s@." msg; | ||
| Format.eprintf | ||
| "res-to-affine: no translation emitted for %s@." input; | ||
|
|
@@ -88,18 +100,38 @@ let run engine grammar_dir do_translate do_partial input output_opt = | |
| else | ||
| Emitter.emit ~module_name ~source_path:input ~source ~findings | ||
| in | ||
| match output_opt with | ||
| | None -> | ||
| print_string out | ||
| | Some path -> | ||
| write_file path out; | ||
| Format.printf | ||
| "res-to-affine: %d finding%s, %d translated [%s] → %s@." | ||
| (List.length findings) | ||
| (if List.length findings = 1 then "" else "s") | ||
| (List.length translated) | ||
| (engine_label engine) | ||
| path | ||
| (match output_opt with | ||
| | None -> | ||
| print_string out | ||
| | Some path -> | ||
| write_file path out; | ||
| (* Report the EFFECTIVE engine, not the requested one. *) | ||
| Format.printf | ||
| "res-to-affine: %d finding%s, %d translated [%s] → %s@." | ||
| (List.length findings) | ||
| (if List.length findings = 1 then "" else "s") | ||
| (List.length translated) | ||
| (if !degraded then "scanner (DEGRADED)" else engine_label engine) | ||
| path); | ||
|
|
||
| (* Fail loudly rather than at exit 0. A sweep over hundreds of files cannot | ||
| otherwise distinguish a real port from a function-free skeleton, and the | ||
| stderr warning scrolls past. metadatastician/stapeln migrated all 47 of | ||
| its frontend modules this way: every file reported success, every file | ||
| contained zero functions. *) | ||
| if !degraded && not allow_fallback then begin | ||
| Format.eprintf | ||
| "res-to-affine: DEGRADED OUTPUT for %s — the walker engine was \ | ||
| unavailable, so no functions were translated.@." input; | ||
| Format.eprintf | ||
| "res-to-affine: install the grammar (`just install-grammar`) or pass \ | ||
| `--grammar-dir`; note the default path is resolved relative to the \ | ||
| CURRENT DIRECTORY, so run this from the affinescript repo root.@."; | ||
| Format.eprintf | ||
| "res-to-affine: pass `--allow-scanner-fallback` if a \ | ||
| declarations-only skeleton really is what you want.@."; | ||
| exit 3 | ||
| end | ||
|
Comment on lines
+103
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Effective-engine label and remediation text are inaccurate when only translation fails. The single In the third case, findings are produced by the real walker, not the scanner. The summary nonetheless reports Track the actual cause (for example with a small variant such as Sketch of a cause-aware alternative- let degraded = ref false in
+ type degraded_reason =
+ | Not_degraded
+ | Walker_scan_failed
+ | Scanner_chosen_for_translate
+ | Walker_translate_failed
+ let degraded = ref Not_degraded in
@@
- | Failure msg ->
- degraded := true;
+ | Failure msg ->
+ degraded := Walker_scan_failed;
@@
- degraded := true;
+ degraded := Scanner_chosen_for_translate;
@@
- degraded := true;
+ degraded := Walker_translate_failed;Then branch the summary label and the exit-3 stderr guidance on the reason instead of on a bare boolean. 🤖 Prompt for AI Agents |
||
|
|
||
| (* ---- cmdliner wiring ---- *) | ||
|
|
||
|
|
@@ -159,13 +191,22 @@ let partial_arg = | |
| in | ||
| Cmdliner.Arg.(value & flag & info ["partial"] ~doc) | ||
|
|
||
| let allow_fallback_arg = | ||
| let doc = | ||
| "Exit 0 even when the walker engine was unavailable and the scanner \ | ||
| produced the output. Without this, a degraded run exits 3, because a \ | ||
| declarations-only skeleton with no functions is almost never what a \ | ||
| migration sweep wants and the stderr warning is easy to miss." | ||
| in | ||
| Cmdliner.Arg.(value & flag & info ["allow-scanner-fallback"] ~doc) | ||
|
|
||
| let cmd = | ||
| let doc = "Emit an AffineScript skeleton from a ReScript source file." in | ||
| let info = Cmdliner.Cmd.info "res-to-affine" ~version:"0.1.0" ~doc in | ||
| let term = | ||
| Cmdliner.Term.( | ||
| const run $ engine_arg $ grammar_dir_arg $ translate_arg $ partial_arg | ||
| $ input_arg $ output_arg) | ||
| $ allow_fallback_arg $ input_arg $ output_arg) | ||
| in | ||
| Cmdliner.Cmd.v info term | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚪ LOW RISK
Nitpick: Calculate the length of 'findings' once and store it in a variable to avoid redundant O(n) traversals.