internal/core/adt: improve error message for optional fields
Fixes #455
Change-Id: Ic1d2a14541a9b6507db64502a56891c92a382e32
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7802
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git "a/cue/testdata/fulleval/029_Issue_\04394.txtar" "b/cue/testdata/fulleval/029_Issue_\04394.txtar"
index 231bb0d..ba23b79 100644
--- "a/cue/testdata/fulleval/029_Issue_\04394.txtar"
+++ "b/cue/testdata/fulleval/029_Issue_\04394.txtar"
@@ -97,7 +97,7 @@
}
select: (struct){
opt: (_|_){
- // [incomplete] select.opt: undefined field opt:
+ // [incomplete] select.opt: cannot reference optional field opt:
// ./in.cue:10:15
}
txt: (int){ 2 }
@@ -107,7 +107,7 @@
}
index: (struct){
opt: (_|_){
- // [incomplete] index.opt: undefined field opt:
+ // [incomplete] index.opt: cannot reference optional field opt:
// ./in.cue:17:15
}
txt: (int){ 2 }
diff --git a/cue/testdata/references/optional.txtar b/cue/testdata/references/optional.txtar
index 2b1f3f3..804b07a 100644
--- a/cue/testdata/references/optional.txtar
+++ b/cue/testdata/references/optional.txtar
@@ -16,7 +16,7 @@
(struct){
a: (struct){
b: (_|_){
- // [incomplete] a.b: undefined field foo:
+ // [incomplete] a.b: cannot reference optional field foo:
// ./in.cue:4:8
}
}
diff --git a/cue/testdata/resolve/010_optional_field_resolves_to_incomplete.txtar b/cue/testdata/resolve/010_optional_field_resolves_to_incomplete.txtar
index 50df2cb..d69371d 100644
--- a/cue/testdata/resolve/010_optional_field_resolves_to_incomplete.txtar
+++ b/cue/testdata/resolve/010_optional_field_resolves_to_incomplete.txtar
@@ -29,11 +29,11 @@
(struct){
r: (struct){
b: (_|_){
- // [incomplete] r.b: undefined field a:
+ // [incomplete] r.b: cannot reference optional field a:
// ./in.cue:3:6
}
c: (_|_){
- // [incomplete] r.c: undefined field a:
+ // [incomplete] r.c: cannot reference optional field a:
// ./in.cue:4:8
}
}
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index c7c9bdf..6545899 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -426,6 +426,10 @@
// OptionalTypes returns a bit field with the type of optional constraints
// that are represented by this Acceptor.
OptionalTypes() OptionalType
+
+ // IsOptional reports whether a field is explicitly defined as optional,
+ // as opposed to whether it is allowed by a pattern constraint.
+ IsOptional(f Feature) bool
}
// OptionalType is a bit field of the type of optional constraints in use by an
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 1b9e3d6..fc19b80 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -637,7 +637,12 @@
c.addErrf(code, pos, "index out of range [%d] with length %d",
l.Index(), len(x.Elems()))
} else {
- c.addErrf(code, pos, "undefined field %s", label)
+ if code != 0 && x.Closed != nil && x.Closed.IsOptional(l) {
+ c.addErrf(code, pos,
+ "cannot reference optional field %s", label)
+ } else {
+ c.addErrf(code, pos, "undefined field %s", label)
+ }
}
}
return a
diff --git a/internal/core/eval/closed.go b/internal/core/eval/closed.go
index 8432e79..786ebfa 100644
--- a/internal/core/eval/closed.go
+++ b/internal/core/eval/closed.go
@@ -126,6 +126,14 @@
return mask
}
+func (a *acceptor) IsOptional(label adt.Feature) bool {
+ optional := false
+ a.visitAllFieldSets(func(f *fieldSet) {
+ optional = optional || f.IsOptional(label)
+ })
+ return optional
+}
+
// A disjunction acceptor represents a disjunction of all possible fields. Note
// that this is never used in evaluation as evaluation stops at incomplete nodes
// and a disjunction is incomplete. When the node is referenced, the original
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index 47ffe47..b7ea096 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -66,6 +66,15 @@
return mask
}
+func (o *fieldSet) IsOptional(label adt.Feature) bool {
+ for _, f := range o.fields {
+ if f.label == label && len(f.optional) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
type field struct {
label adt.Feature
optional []adt.Node