Skip to content

fix: Preserve error order when deduplicating errors #3860

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/cue/cmd/testdata/script/cmd_err.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
cmp stderr cmd_badfields.out

-- cmd_badfields.out --
command.ref.task.display.contents: invalid bytes argument: non-concrete value (string|bytes):
./task_tool.cue:6:8
tool/file:17:3
command.ref.task.display.filename: invalid string argument: non-concrete value string:
./task_tool.cue:6:8
./task_tool.cue:7:9
tool/file:15:3
tool/file:15:16
command.ref.task.display.contents: invalid bytes argument: non-concrete value (string|bytes):
./task_tool.cue:6:8
tool/file:17:3
-- task_tool.cue --
package home

Expand Down
8 changes: 4 additions & 4 deletions cmd/cue/cmd/testdata/script/cmd_errpos.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ cmp stderr expect-stderr

-- expect-stdout --
-- expect-stderr --
command.prompter.filename: invalid string argument: non-concrete value string:
./task_tool.cue:9:10
tool/file:9:3
tool/file:9:16
command.prompter.contents: invalid bytes argument: non-concrete value string:
./task_tool.cue:9:10
./task_tool.cue:12:13
./task_tool.cue:17:3
tool/file:11:3
command.prompter.filename: invalid string argument: non-concrete value string:
./task_tool.cue:9:10
tool/file:9:3
tool/file:9:16
-- task_tool.cue --
package foo

Expand Down
4 changes: 2 additions & 2 deletions cmd/cue/cmd/testdata/script/def_jsonschema.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ import "strings"
"foo": true
}
-- expect-stderr-strict --
keyword "$dynamicAnchor" not yet implemented:
./bad.json:4:3
unknown keyword "foo":
./bad.json:5:3
keyword "$dynamicAnchor" not yet implemented:
./bad.json:4:3
-- expect-stderr-strict-features --
keyword "$dynamicAnchor" not yet implemented:
./bad.json:4:3
Expand Down
6 changes: 3 additions & 3 deletions cmd/cue/cmd/testdata/script/eval_errs.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ cmp stdout expect-stdout

-- expect-stdout --
-- expect-stderr --
x.q: conflicting values "goodbye" and "hello":
./errs.cue:1:4
./errs.cue:2:4
bar: 2 errors in empty disjunction:
bar.a: conflicting values "str" and int (mismatched types string and int):
./errs.cue:5:10
./errs.cue:6:16
bar.b: conflicting values 2 and string (mismatched types int and string):
./errs.cue:5:21
./errs.cue:6:26
x.q: conflicting values "goodbye" and "hello":
./errs.cue:1:4
./errs.cue:2:4
-- errs.cue --
a: "hello"
b: "goodbye"
Expand Down
42 changes: 21 additions & 21 deletions cmd/cue/cmd/testdata/script/exp_gengotypes.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ _typeTests: [...#typeTest] & [
{name: "LinkedList", fail: both: {next: "x"}},
]
-- cuetest/fail_check.stderr --
fail.both.notString: conflicting values "not_a_struct" and {embedded2?:int} (mismatched types string and struct):
./cuetest/all.cue:4:24
./root/root.cue:95:2
fail.both.notList: conflicting values [1,2,3] and {embedded2?:int} (mismatched types list and struct):
./cuetest/all.cue:5:24
./root/root.cue:95:2
fail.both."16_IntList".types.IntList.0: conflicting values "foo" and int (mismatched types string and int):
./cuetest/all.cue:67:40
./root/types.cue:26:23
Expand All @@ -184,16 +190,22 @@ fail.both."40_NonEmptyString".types.NonEmptyString: conflicting values string an
fail.both."42_LinkedList".types.LinkedList.next: conflicting values "x" and {item?:_,next?:#linkedList} (mismatched types string and struct):
./cuetest/all.cue:94:50
./root/types.cue:43:14
fail.both.notList: conflicting values [1,2,3] and {embedded2?:int} (mismatched types list and struct):
./cuetest/all.cue:5:24
./root/root.cue:95:2
fail.both.notString: conflicting values "not_a_struct" and {embedded2?:int} (mismatched types string and struct):
./cuetest/all.cue:4:24
./root/root.cue:95:2
fail.cue.isNotEqual.mustEqual2: conflicting values 8 and 99:
./cuetest/all.cue:11:37
./cuetest/all.cue:11:52
fail.cue.discBoth.discriminatorField: 2 errors in empty disjunction:
fail.cue.discBoth.discriminatorField.two: field not allowed:
./cuetest/all.cue:14:51
fail.cue.discBoth.discriminatorField.one: field not allowed:
./cuetest/all.cue:14:43
fail.cue."9_Uint".types.Uint: invalid value -34 (out of bound >=0):
./cuetest/all.cue:59:30
fail.cue."11_Int8".types.Int8: invalid value 99999 (out of bound <=127):
./cuetest/all.cue:61:30
fail.cue."12_Int8".types.Int8: invalid value -99999 (out of bound >=-128):
./cuetest/all.cue:62:30
fail.cue."18_IntListClosed2".types.IntListClosed2: incompatible list lengths (2 and 4):
./cuetest/all.cue:69:38
fail.cue."29_NullOrStruct".types.NullOrStruct: 2 errors in empty disjunction:
fail.cue."29_NullOrStruct".types.NullOrStruct: conflicting values "foo" and null (mismatched types string and null):
./cuetest/all.cue:81:43
Expand All @@ -208,21 +220,6 @@ fail.cue."32_NullOrString".types.NullOrString: conflicting values 123 and null (
fail.cue."32_NullOrString".types.NullOrString: conflicting values 123 and string (mismatched types int and string):
./cuetest/all.cue:84:43
./root/types.cue:36:30
fail.cue."39_UniqueStrings".types.UniqueStrings: invalid value ["foo","foo"] (does not satisfy list.UniqueItems): equal value ("foo") at position 0 and 1:
./cuetest/all.cue:25:55
./root/types.cue:39:23
fail.cue."9_Uint".types.Uint: invalid value -34 (out of bound >=0):
./cuetest/all.cue:59:30
fail.cue.discBoth.discriminatorField: 2 errors in empty disjunction:
fail.cue.discBoth.discriminatorField.one: field not allowed:
./cuetest/all.cue:14:43
fail.cue.discBoth.discriminatorField.two: field not allowed:
./cuetest/all.cue:14:51
fail.cue.isNotEqual.mustEqual2: conflicting values 8 and 99:
./cuetest/all.cue:11:37
./cuetest/all.cue:11:52
fail.cue."18_IntListClosed2".types.IntListClosed2: incompatible list lengths (2 and 4):
./cuetest/all.cue:69:38
fail.cue."34_NumericBounds".types.NumericBounds: invalid value 5555 (out of bound <100):
./root/types.cue:37:28
./cuetest/all.cue:86:43
Expand All @@ -231,6 +228,9 @@ fail.cue."36_NonEmptyString".types.NonEmptyString: invalid value "" (out of boun
./cuetest/all.cue:25:55
./cuetest/all.cue:88:43
./root/types.cue:38:23
fail.cue."39_UniqueStrings".types.UniqueStrings: invalid value ["foo","foo"] (does not satisfy list.UniqueItems): equal value ("foo") at position 0 and 1:
./cuetest/all.cue:25:55
./root/types.cue:39:23
-- go.mod --
module "foo.test/bar"

Expand Down
4 changes: 2 additions & 2 deletions cmd/cue/cmd/testdata/script/export_required.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ a: x?: int // do not include this position

b: #Def
-- stderr.golden --
#Def.x: field is required but not present:
./y.cue:5:2
a.x: field is required but not present:
./y.cue:1:4
#Def.x: field is required but not present:
./y.cue:5:2
2 changes: 1 addition & 1 deletion cmd/cue/cmd/testdata/script/load_underscore.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ cmp stderr want-stderr
! exec cue export other.example/m:_
cmp stderr want-stderr-2
-- want-stderr --
test.example/foo/foo@v0: import failed: _ is not a valid import path qualifier in "other.example/m:_":
test.example/foo/foo@v0: import failed: cannot find package "other.example/m": _ is not a valid import path qualifier in "other.example/m:_":
./foo/foo.cue:8:8
-- want-stderr-2 --
invalid import path qualifier _ in "other.example/m:_"
Expand Down
4 changes: 2 additions & 2 deletions cmd/cue/cmd/testdata/script/vet_concrete.txtar
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
! exec cue vet -c
cmp stderr expect-stderr
-- expect-stderr --
b.str: incomplete value string:
./partial.cue:8:7
sum: incomplete value 1 | 2
b.idx: invalid non-ground value string (must be concrete string):
./partial.cue:8:7
b.str: incomplete value string:
./partial.cue:8:7
-- partial.cue --
package partial

Expand Down
6 changes: 3 additions & 3 deletions cmd/cue/cmd/testdata/script/vet_path.txtar
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
! exec cue vet -l 'strings.ToLower(kind)' -l name services.jsonl services.cue
cmp stderr expect-stderr
-- expect-stderr --
deployment.Booster.name: invalid value "Booster" (out of bound !~"^[A-Z]"):
./services.cue:1:29
./services.jsonl:7:13
service."Supplement\nfoo".name: invalid value "Supplement\nfoo" (out of bound !~"^[A-Z]"):
./services.cue:2:26
./services.jsonl:12:13
deployment.Booster.name: invalid value "Booster" (out of bound !~"^[A-Z]"):
./services.cue:1:29
./services.jsonl:7:13
-- services.cue --
deployment: [string]: name: !~"^[A-Z]"
service: [string]: name: !~"^[A-Z]"
Expand Down
29 changes: 15 additions & 14 deletions cue/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,7 @@ func (p list) sanitize() list {
if p == nil {
return p
}
a := slices.Clone(p)
a.RemoveMultiples()
return a
return p.RemoveMultiples()
}

// Sort sorts an List. *posError entries are sorted by position,
Expand All @@ -417,19 +415,22 @@ func (p list) Sort() {
})
}

// RemoveMultiples sorts an List and removes all but the first error per line.
func (p *list) RemoveMultiples() {
p.Sort()
var last Error
i := 0
for _, e := range *p {
if last == nil || !approximateEqual(last, e) {
last = e
(*p)[i] = e
i++
// RemoveMultiples removes all but the first instance of an error message per line.
func (p list) RemoveMultiples() list {
deduplicated := slices.Clone(p)

deduplicatedIdx := 0
OUTER:
for _, element := range p {
for _, deduplicatedElement := range deduplicated[0:deduplicatedIdx] {
if approximateEqual(deduplicatedElement, element) {
continue OUTER
}
}
deduplicated[deduplicatedIdx] = element
deduplicatedIdx++
}
(*p) = (*p)[0:i]
return deduplicated[0:deduplicatedIdx]
}

func approximateEqual(a, b Error) bool {
Expand Down
37 changes: 34 additions & 3 deletions cue/errors/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package errors
import (
"bytes"
"fmt"
"slices"
"testing"

"cuelang.org/go/cue/token"
Expand Down Expand Up @@ -79,14 +80,44 @@ func TestErrorList_Sort(t *testing.T) {
}

func TestErrorList_RemoveMultiples(t *testing.T) {
error1 := Promote(fmt.Errorf("error1"), "msg1")
error2 := Promote(fmt.Errorf("error2"), "msg2")

tests := []struct {
name string
p *list
p list
want list
}{
// TODO: Add test cases.
{
name: "SingleError",
p: list{error1},
want: list{error1},
},
{
name: "TwoErrorsNoDuplicatesOrder12",
p: list{error1, error2},
want: list{error1, error2},
},
{
name: "TwoErrorsNoDuplicatesOrder21",
p: list{error2, error1},
want: list{error2, error1},
},
{
name: "TwoErrorsDuplicates",
p: list{error1, error1},
want: list{error1},
},
{
name: "ThreeErrorsPreserveOrder",
p: list{error1, error2, error1},
want: list{error1, error2},
},
}
for _, tt := range tests {
tt.p.RemoveMultiples()
if got := tt.p.RemoveMultiples(); !slices.Equal(got, tt.want) {
t.Errorf("%q. list.RemoveMultiples() list = %v, want = %v", tt.name, got, tt.want)
}
}
}

Expand Down
16 changes: 8 additions & 8 deletions cue/load/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ func TestLoad(t *testing.T) {
cfg: badModCfg,
args: []string{"."},
want: `err: module: 2 errors in empty disjunction:
module: conflicting values 123 and "" (mismatched types int and string):
$CWD/testdata/badmod/cue.mod/module.cue:2:9
cuelang.org/go/mod/modfile/schema.cue:56:22
module: conflicting values 123 and string (mismatched types int and string):
$CWD/testdata/badmod/cue.mod/module.cue:2:9
cuelang.org/go/mod/modfile/schema.cue:98:12
module: conflicting values 123 and "" (mismatched types int and string):
$CWD/testdata/badmod/cue.mod/module.cue:2:9
cuelang.org/go/mod/modfile/schema.cue:56:22
path: ""
module: ""
root: ""
Expand Down Expand Up @@ -215,8 +215,8 @@ files:
name: "BadIdentifier",
cfg: dirCfg,
args: []string{"foo.com/bad-identifier"},
want: `err: cannot determine package name for "foo.com/bad-identifier"; set it explicitly with ':'
cannot find package "foo.com/bad-identifier": cannot find module providing package foo.com/bad-identifier
want: `err: cannot find package "foo.com/bad-identifier": cannot find module providing package foo.com/bad-identifier
cannot determine package name for "foo.com/bad-identifier"; set it explicitly with ':'
path: foo.com/bad-identifier
module: ""
root: $CWD/testdata/testmod
Expand Down Expand Up @@ -335,11 +335,11 @@ files:
Tags: []string{"prod"},
},
args: []string{"./tagsbad"},
want: `err: tag "prod" not used in any file
want: `err: multiple @if attributes:
$CWD/testdata/testmod/tagsbad/prod.cue:2:1
previous declaration here:
$CWD/testdata/testmod/tagsbad/prod.cue:1:1
multiple @if attributes:
$CWD/testdata/testmod/tagsbad/prod.cue:2:1
tag "prod" not used in any file
path: mod.test/test/tagsbad@v0
module: mod.test/test@v0
root: $CWD/testdata/testmod
Expand Down
6 changes: 3 additions & 3 deletions cue/testdata/basicrewrite/001_regexp.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ Conjuncts: 25
Disjuncts: 16
-- out/eval --
Errors:
e3: conflicting values !="a" and <5 (mismatched types string and number):
./in.cue:22:5
./in.cue:22:13
b3: invalid value "foo" (out of bound =~"[a-z]{4}"):
./in.cue:10:5
./in.cue:11:5
Expand All @@ -86,6 +83,9 @@ e1: cannot use 1 (type int) as type (string|bytes):
e2: cannot use true (type bool) as type (string|bytes):
./in.cue:21:5
./in.cue:21:14
e3: conflicting values !="a" and <5 (mismatched types string and number):
./in.cue:22:5
./in.cue:22:13

Result:
(_|_){
Expand Down
4 changes: 2 additions & 2 deletions cue/testdata/basicrewrite/015_types.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ Conjuncts: 14
Disjuncts: 10
-- out/eval --
Errors:
b: invalid operand int ('!' requires concrete value):
./in.cue:7:6
e: conflicting values int and string (mismatched types int and string):
./in.cue:5:5
./in.cue:5:11
e2: conflicting values 1 and string (mismatched types int and string):
./in.cue:6:5
./in.cue:6:9
b: invalid operand int ('!' requires concrete value):
./in.cue:7:6
p: invalid operation +true (+ bool):
./in.cue:8:5
m: invalid operation -false (- bool):
Expand Down
Loading