Skip to content

Commit d32ec51

Browse files
committed
test(cli): pin stopping script compilation after an ICE
Extract the script iteration policy into a small helper used by the CLI. The regression test proves ordinary compile errors continue while an internal compiler error stops before the next script can reuse poisoned compiler state.
1 parent 54f3aff commit d32ec51

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

ice_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,30 @@ func TestCleanupUnlessReleasedPreservesReturnedModule(t *testing.T) {
9090
t.Fatal("cleanup ran after module ownership was returned to the caller")
9191
}
9292
}
93+
94+
func TestCompileScriptsStopsAfterICE(t *testing.T) {
95+
scripts := []string{"ordinary.spt", "panic.spt", "after.spt"}
96+
var visited []string
97+
98+
compileErr, stopped := compileScriptsUntilICE(scripts, func(script string) error {
99+
visited = append(visited, script)
100+
switch script {
101+
case "ordinary.spt":
102+
return errors.New("ordinary compile error")
103+
case "panic.spt":
104+
return &internalCompilerError{unit: script}
105+
default:
106+
return nil
107+
}
108+
})
109+
110+
if compileErr != 2 {
111+
t.Fatalf("compile errors = %d, want 2", compileErr)
112+
}
113+
if !stopped {
114+
t.Fatal("compilation did not stop after an internal compiler error")
115+
}
116+
if got := strings.Join(visited, ","); got != "ordinary.spt,panic.spt" {
117+
t.Fatalf("visited scripts = %q, want ordinary error followed by ICE only", got)
118+
}
119+
}

main.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,18 @@ func isInternalCompilerError(err error) bool {
228228
return errors.As(err, &ice)
229229
}
230230

231+
func compileScriptsUntilICE(scriptFiles []string, compile func(string) error) (compileErr int, stopped bool) {
232+
for _, scriptFile := range scriptFiles {
233+
if err := compile(scriptFile); err != nil {
234+
compileErr++
235+
if isInternalCompilerError(err) {
236+
return compileErr, true
237+
}
238+
}
239+
}
240+
return compileErr, false
241+
}
242+
231243
// recoverICE converts a compiler panic into an internal-compiler-error report.
232244
// The typed error tells the driver to stop before reusing compiler state that
233245
// may have been left inconsistent by the panic.
@@ -628,23 +640,17 @@ func runCompile(opts cliOptions) {
628640
return
629641
}
630642

631-
compileErr := 0
632643
binErr := 0
633644
funcCache := make(map[string]*compiler.Func)
634645
exprCache := make(map[compiler.ExprKey]*compiler.ExprInfo)
635-
for _, scriptFile := range scriptFiles {
646+
compileErr, stoppedOnICE := compileScriptsUntilICE(scriptFiles, func(scriptFile string) error {
636647
script := strings.TrimSuffix(filepath.Base(scriptFile), SPT_SUFFIX)
637648
fmt.Println("🛠️ Starting compile for script: " + script)
638649
scriptModule, err := p.CompileScript(scriptFile, script, codeCompiler, codeLL, funcCache, exprCache)
639650
if err != nil {
640651
fmt.Println(err)
641652
fmt.Printf("⛓️‍💥 Error while trying to compile %s\n", script)
642-
compileErr++
643-
if isInternalCompilerError(err) {
644-
fmt.Println("Stopping compilation after internal compiler error.")
645-
break
646-
}
647-
continue
653+
return err
648654
}
649655

650656
err = func() error {
@@ -657,6 +663,10 @@ func runCompile(opts cliOptions) {
657663
} else {
658664
fmt.Printf("✅ Successfully built binary for script: %s\n", script)
659665
}
666+
return nil
667+
})
668+
if stoppedOnICE {
669+
fmt.Println("Stopping compilation after internal compiler error.")
660670
}
661671
if compileErr > 0 || binErr > 0 {
662672
os.Exit(1)

0 commit comments

Comments
 (0)