From 55eeb50fb4afb9808e51a39315410e865f9035e4 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Sat, 7 Jun 2025 00:23:45 -0400 Subject: [PATCH] lang: Refactor all the highlight helping together Keep this cleaner and add a bit more. --- lang/ast/structs.go | 13 +++++++------ lang/ast/util.go | 13 ------------- lang/interfaces/textarea.go | 13 +++++++++++++ lang/interpret_test/TestAstFunc1/graph8.txtar | 2 +- lang/interpret_test/TestAstFunc1/importscope1.txtar | 2 +- .../TestAstFunc2/class-capture7.txtar | 2 +- .../TestAstFunc2/class-include-as-class2.txtar | 2 +- .../TestAstFunc2/clear-env-on-var.txtar | 2 +- lang/interpret_test/TestAstFunc2/func-gen1.txtar | 2 +- lang/interpret_test/TestAstFunc2/func-gen2.txtar | 2 +- .../TestAstFunc2/import-scope-vars2-fail.txtar | 2 +- lang/unification/fastsolver/fastsolver.go | 8 +++++++- 12 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lang/ast/structs.go b/lang/ast/structs.go index 8662cd5c..60ad9066 100644 --- a/lang/ast/structs.go +++ b/lang/ast/structs.go @@ -6854,7 +6854,7 @@ func (obj *StmtInclude) SetScope(scope *interfaces.Scope) error { classScopeFeedback(scope, obj.data.Logf) } err := fmt.Errorf("class `%s` does not exist in this scope", obj.Name) - return highlightHelper(obj, obj.data.Logf, err) + return interfaces.HighlightHelper(obj, obj.data.Logf, err) } class, ok := stmt.(*StmtClass) if !ok { @@ -6863,7 +6863,8 @@ func (obj *StmtInclude) SetScope(scope *interfaces.Scope) error { // Is it even possible for the signatures to not match? if len(class.Args) != len(obj.Args) { - return fmt.Errorf("class `%s` expected %d args but got %d", obj.Name, len(class.Args), len(obj.Args)) + err := fmt.Errorf("class `%s` expected %d args but got %d", obj.Name, len(class.Args), len(obj.Args)) + return interfaces.HighlightHelper(obj, obj.data.Logf, err) } if obj.class != nil { @@ -10514,7 +10515,7 @@ func (obj *ExprCall) SetScope(scope *interfaces.Scope, sctx map[string]interface lambdaScopeFeedback(obj.scope, obj.data.Logf) } err := fmt.Errorf("lambda `$%s` does not exist in this scope", prefixedName) - return highlightHelper(obj, obj.data.Logf, err) + return interfaces.HighlightHelper(obj, obj.data.Logf, err) } target = f } @@ -10531,7 +10532,7 @@ func (obj *ExprCall) SetScope(scope *interfaces.Scope, sctx map[string]interface functionScopeFeedback(obj.scope, obj.data.Logf) } err := fmt.Errorf("func `%s` does not exist in this scope", prefixedName) - return highlightHelper(obj, obj.data.Logf, err) + return interfaces.HighlightHelper(obj, obj.data.Logf, err) } target = f } @@ -11284,7 +11285,7 @@ func (obj *ExprVar) SetScope(scope *interfaces.Scope, sctx map[string]interfaces variableScopeFeedback(obj.scope, obj.data.Logf) } err := fmt.Errorf("var `$%s` does not exist in this scope", obj.Name) - return highlightHelper(obj, obj.data.Logf, err) + return interfaces.HighlightHelper(obj, obj.data.Logf, err) } obj.scope.Variables[obj.Name] = target @@ -11345,7 +11346,7 @@ func (obj *ExprVar) Infer() (*types.Type, []*interfaces.UnificationInvariant, er expr, exists := obj.scope.Variables[obj.Name] if !exists { err := fmt.Errorf("var `%s` does not exist in this scope", obj.Name) - return nil, nil, highlightHelper(obj, obj.data.Logf, err) + return nil, nil, interfaces.HighlightHelper(obj, obj.data.Logf, err) } // This child call to Infer is an outlier to the common pattern where diff --git a/lang/ast/util.go b/lang/ast/util.go index b07bfdc4..78ca62bb 100644 --- a/lang/ast/util.go +++ b/lang/ast/util.go @@ -574,16 +574,3 @@ func classScopeFeedback(scope *interfaces.Scope, logf func(format string, v ...i logf("class %s", name) } } - -// highlightHelper give the user better file/line number feedback. -func highlightHelper(node interfaces.Node, logf func(format string, v ...interface{}), err error) error { - displayer, ok := node.(interfaces.TextDisplayer) - if ok { - if highlight := displayer.HighlightText(); highlight != "" { - logf("%s: %s", err.Error(), highlight) - } - //return fmt.Errorf("%s: %s", err.Error(), displayer.Byline()) - } - - return err -} diff --git a/lang/interfaces/textarea.go b/lang/interfaces/textarea.go index 209b9137..1dd61286 100644 --- a/lang/interfaces/textarea.go +++ b/lang/interfaces/textarea.go @@ -197,3 +197,16 @@ func (obj *Textarea) HighlightText() string { return result.String() } + +// HighlightHelper gives the user better file/line number feedback. +func HighlightHelper(node Node, logf func(format string, v ...interface{}), err error) error { + displayer, ok := node.(TextDisplayer) + if !ok { + return err + } + + if highlight := displayer.HighlightText(); highlight != "" { + logf("%s: %s", err.Error(), highlight) + } + return fmt.Errorf("%s: %s", err.Error(), displayer.Byline()) +} diff --git a/lang/interpret_test/TestAstFunc1/graph8.txtar b/lang/interpret_test/TestAstFunc1/graph8.txtar index a0d78a2e..dd075b79 100644 --- a/lang/interpret_test/TestAstFunc1/graph8.txtar +++ b/lang/interpret_test/TestAstFunc1/graph8.txtar @@ -6,4 +6,4 @@ if true { $b = true } -- OUTPUT -- -# err: errSetScope: var `$b` does not exist in this scope +# err: errSetScope: var `$b` does not exist in this scope: /main.mcl @ 2:4-2:5 diff --git a/lang/interpret_test/TestAstFunc1/importscope1.txtar b/lang/interpret_test/TestAstFunc1/importscope1.txtar index 035c4fc0..f75cca24 100644 --- a/lang/interpret_test/TestAstFunc1/importscope1.txtar +++ b/lang/interpret_test/TestAstFunc1/importscope1.txtar @@ -16,4 +16,4 @@ class xclass { } } -- OUTPUT -- -# err: errSetScope: func `os.is_family_debian` does not exist in this scope +# err: errSetScope: func `os.is_family_debian` does not exist in this scope: /second.mcl @ 5:12-5:33 diff --git a/lang/interpret_test/TestAstFunc2/class-capture7.txtar b/lang/interpret_test/TestAstFunc2/class-capture7.txtar index 778d7f8a..77aa62ba 100644 --- a/lang/interpret_test/TestAstFunc2/class-capture7.txtar +++ b/lang/interpret_test/TestAstFunc2/class-capture7.txtar @@ -9,4 +9,4 @@ include foo # This sort of thing is not currently supported, and not sure if it ever will. include bar # nope! -- OUTPUT -- -# err: errSetScope: class `bar` does not exist in this scope +# err: errSetScope: class `bar` does not exist in this scope: /main.mcl @ 9:1-9:12 diff --git a/lang/interpret_test/TestAstFunc2/class-include-as-class2.txtar b/lang/interpret_test/TestAstFunc2/class-include-as-class2.txtar index 156b8e74..9b478d04 100644 --- a/lang/interpret_test/TestAstFunc2/class-include-as-class2.txtar +++ b/lang/interpret_test/TestAstFunc2/class-include-as-class2.txtar @@ -14,4 +14,4 @@ class c1($b) { include c1 as i1 include i1.inner -- OUTPUT -- -# err: errSetScope: class `c1` expected 1 args but got 0 +# err: errSetScope: class `c1` expected 1 args but got 0: /main.mcl @ 13:1-13:17 diff --git a/lang/interpret_test/TestAstFunc2/clear-env-on-var.txtar b/lang/interpret_test/TestAstFunc2/clear-env-on-var.txtar index 200eaab5..f8c52549 100644 --- a/lang/interpret_test/TestAstFunc2/clear-env-on-var.txtar +++ b/lang/interpret_test/TestAstFunc2/clear-env-on-var.txtar @@ -7,4 +7,4 @@ $f = func($x) { $name = $f("foo") test "${name}" {} -- OUTPUT -- -# err: errSetScope: var `$x` does not exist in this scope +# err: errSetScope: var `$x` does not exist in this scope: /main.mcl @ 1:8-1:9 diff --git a/lang/interpret_test/TestAstFunc2/func-gen1.txtar b/lang/interpret_test/TestAstFunc2/func-gen1.txtar index 1d71cb8a..ddfc2873 100644 --- a/lang/interpret_test/TestAstFunc2/func-gen1.txtar +++ b/lang/interpret_test/TestAstFunc2/func-gen1.txtar @@ -11,4 +11,4 @@ include funcgen1 $x1 = fun1() # not funcgen1.fun1 since it's *not* an import! test "${x1}" {} # hi -- OUTPUT -- -# err: errSetScope: func `fun1` does not exist in this scope +# err: errSetScope: func `fun1` does not exist in this scope: /main.mcl @ 10:7-10:13 diff --git a/lang/interpret_test/TestAstFunc2/func-gen2.txtar b/lang/interpret_test/TestAstFunc2/func-gen2.txtar index ad152066..4fe9540d 100644 --- a/lang/interpret_test/TestAstFunc2/func-gen2.txtar +++ b/lang/interpret_test/TestAstFunc2/func-gen2.txtar @@ -14,4 +14,4 @@ include funcgen2 $x2 = fun2() # not funcgen2.fun2 since it's *not* an import! test "${x2}" {} # hello world -- OUTPUT -- -# err: errSetScope: func `fun2` does not exist in this scope +# err: errSetScope: func `fun2` does not exist in this scope: /main.mcl @ 13:7-13:13 diff --git a/lang/interpret_test/TestAstFunc2/import-scope-vars2-fail.txtar b/lang/interpret_test/TestAstFunc2/import-scope-vars2-fail.txtar index 0934c9f8..51fa06d6 100644 --- a/lang/interpret_test/TestAstFunc2/import-scope-vars2-fail.txtar +++ b/lang/interpret_test/TestAstFunc2/import-scope-vars2-fail.txtar @@ -11,4 +11,4 @@ $x = "this is x.mcl" import "x.mcl" as f $y = $f.x + " and this is y.mcl" -- OUTPUT -- -# err: errSetScope: var `$g.x` does not exist in this scope +# err: errSetScope: var `$g.x` does not exist in this scope: /main.mcl @ 3:6-3:14 diff --git a/lang/unification/fastsolver/fastsolver.go b/lang/unification/fastsolver/fastsolver.go index fd6818e9..3f5e4d37 100644 --- a/lang/unification/fastsolver/fastsolver.go +++ b/lang/unification/fastsolver/fastsolver.go @@ -143,6 +143,10 @@ func (obj *FastInvariantSolver) Solve(ctx context.Context, data *unification.Dat obj.Logf("%s: %s", err.Error(), highlight) } return nil, fmt.Errorf("%s: %s", err.Error(), displayer.Byline()) + + // XXX: when we have fully populated all the position + // information, we could replace the above code with: + //return nil, interfaces.HighlightHelper(x.Node, obj.Logf, err) } if obj.Debug { e1, e2 := unificationUtil.Extract(x.Expect), unificationUtil.Extract(x.Actual) @@ -165,7 +169,9 @@ func (obj *FastInvariantSolver) Solve(ctx context.Context, data *unification.Dat // TODO: collect all of these errors and return them together? if t1.HasUni() { // || t2.HasUni() - return nil, fmt.Errorf("expr: %s is ambiguous: %s", x.Expr, u(t1)) + err := fmt.Errorf("expr: %s is ambiguous: %s", x.Expr, u(t1)) + return nil, interfaces.HighlightHelper(x.Node, obj.Logf, err) + } //if err := t1.Cmp(t2); err != nil { // for development/debugging