Skip to content

Commit 80ff2bd

Browse files
committed
cue/errors: print relative positions when used as arguments too
Most positions in CUE errors are reported as a list to be printed one per line, which is done using relative paths for the sake of brevity and to make the paths "clickable" on some IDEs. For example: builtin package "notinstd" undefined: ./missing.cue:1:8 However, sometimes positions are also used as part of error messages: list redeclared as imported package name previous declaration at /current/dir/missing.cue:4:1: ./missing.cue:2:8 Teach the cue/errors package to make these message argument positions relative as well, which we can do pretty easily by spotting which of the arguments are of type token.Pos and replacing them. Note that this requires that the positions aren't stringified too early. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Ie25c2018a4fb314e2686793009af931fc5a3d241 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1214612 Reviewed-by: Matthew Sackman <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent 6fd7ee4 commit 80ff2bd

File tree

3 files changed

+49
-30
lines changed

3 files changed

+49
-30
lines changed

cmd/cue/cmd/testdata/script/eval_import_bad.txtar

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
# Unknown and redeclared imports should be reported for anonymous packages too.
22

3-
# TODO: we should not use absolute paths in reported errors, which requires using 'cmpenv' below
4-
53
! exec cue export named.cue
6-
cmpenv stderr named.stderr
4+
cmp stderr named.stderr
75

86
! exec cue export missing.cue
9-
cmpenv stderr missing.stderr
7+
cmp stderr missing.stderr
108

119
-- named.cue --
1210
package p
@@ -19,7 +17,7 @@ list: 123
1917
builtin package "notinstd" undefined:
2018
./named.cue:3:8
2119
list redeclared as imported package name
22-
previous declaration at ${WORK}${/}named.cue:6:1:
20+
previous declaration at ./named.cue:6:1:
2321
./named.cue:4:8
2422
-- missing.cue --
2523
import "notinstd"
@@ -30,5 +28,5 @@ list: 123
3028
builtin package "notinstd" undefined:
3129
./missing.cue:1:8
3230
list redeclared as imported package name
33-
previous declaration at ${WORK}${/}missing.cue:4:1:
31+
previous declaration at ./missing.cue:4:1:
3432
./missing.cue:2:8

cue/errors/errors.go

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -505,12 +505,14 @@ type Config struct {
505505
ToSlash bool
506506
}
507507

508+
var zeroConfig = &Config{}
509+
508510
// Print is a utility function that prints a list of errors to w,
509511
// one error per line, if the err parameter is an List. Otherwise
510512
// it prints the err string.
511513
func Print(w io.Writer, err error, cfg *Config) {
512514
if cfg == nil {
513-
cfg = &Config{}
515+
cfg = zeroConfig
514516
}
515517
for _, e := range list(Errors(err)).sanitize() {
516518
printError(w, e, cfg)
@@ -528,11 +530,11 @@ func Details(err error, cfg *Config) string {
528530
// String generates a short message from a given Error.
529531
func String(err Error) string {
530532
var b strings.Builder
531-
writeErr(&b, err)
533+
writeErr(&b, err, zeroConfig)
532534
return b.String()
533535
}
534536

535-
func writeErr(w io.Writer, err Error) {
537+
func writeErr(w io.Writer, err Error, cfg *Config) {
536538
if path := strings.Join(err.Path(), "."); path != "" {
537539
_, _ = io.WriteString(w, path)
538540
_, _ = io.WriteString(w, ": ")
@@ -542,6 +544,25 @@ func writeErr(w io.Writer, err Error) {
542544
u := errors.Unwrap(err)
543545

544546
msg, args := err.Msg()
547+
548+
// Just like [printError] does when printing one position per line,
549+
// make sure that any position formatting arguments print as relative paths.
550+
//
551+
// Note that [Error.Msg] isn't clear about whether we should treat args as read-only,
552+
// so we make a copy if we need to replace any arguments.
553+
didCopy := false
554+
for i, arg := range args {
555+
if arg, ok := arg.(token.Pos); ok {
556+
if !didCopy {
557+
args = slices.Clone(args)
558+
didCopy = true
559+
}
560+
pos := arg.Position()
561+
pos.Filename = relPath(pos.Filename, cfg)
562+
args[i] = pos
563+
}
564+
}
565+
545566
n, _ := fmt.Fprintf(w, msg, args...)
546567

547568
if u == nil {
@@ -573,7 +594,7 @@ func printError(w io.Writer, err error, cfg *Config) {
573594
}
574595

575596
if e, ok := err.(Error); ok {
576-
writeErr(w, e)
597+
writeErr(w, e, cfg)
577598
} else {
578599
fprintf(w, "%v", err)
579600
}
@@ -586,21 +607,7 @@ func printError(w io.Writer, err error, cfg *Config) {
586607
fprintf(w, ":\n")
587608
for _, p := range positions {
588609
pos := p.Position()
589-
path := pos.Filename
590-
if cfg.Cwd != "" {
591-
if p, err := filepath.Rel(cfg.Cwd, path); err == nil {
592-
path = p
593-
// Some IDEs (e.g. VSCode) only recognize a path if it starts
594-
// with a dot. This also helps to distinguish between local
595-
// files and builtin packages.
596-
if !strings.HasPrefix(path, ".") {
597-
path = fmt.Sprintf(".%c%s", filepath.Separator, path)
598-
}
599-
}
600-
}
601-
if cfg.ToSlash {
602-
path = filepath.ToSlash(path)
603-
}
610+
path := relPath(pos.Filename, cfg)
604611
fprintf(w, " %s", path)
605612
if pos.IsValid() {
606613
if path != "" {
@@ -611,3 +618,21 @@ func printError(w io.Writer, err error, cfg *Config) {
611618
fprintf(w, "\n")
612619
}
613620
}
621+
622+
func relPath(path string, cfg *Config) string {
623+
if cfg.Cwd != "" {
624+
if p, err := filepath.Rel(cfg.Cwd, path); err == nil {
625+
path = p
626+
// Some IDEs (e.g. VSCode) only recognize a path if it starts
627+
// with a dot. This also helps to distinguish between local
628+
// files and builtin packages.
629+
if !strings.HasPrefix(path, ".") {
630+
path = fmt.Sprintf(".%c%s", filepath.Separator, path)
631+
}
632+
}
633+
}
634+
if cfg.ToSlash {
635+
path = filepath.ToSlash(path)
636+
}
637+
return path
638+
}

internal/core/runtime/resolve.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func resolveFile(
9090
if n, ok := fields[name]; ok {
9191
errs = errors.Append(errs, nodeErrorf(spec,
9292
"%s redeclared as imported package name\n"+
93-
"\tprevious declaration at %v", name, lineStr(idx, n)))
93+
"\tprevious declaration at %s", name, n.Pos()))
9494
continue
9595
}
9696
fields[name] = spec
@@ -159,7 +159,3 @@ func resolveFile(
159159
// }
160160
return errs
161161
}
162-
163-
func lineStr(idx *index, n ast.Node) string {
164-
return n.Pos().String()
165-
}

0 commit comments

Comments
 (0)