cue: change Reference to return Instance as well
Change-Id: I33c3479d3f580cfe67cc01fa998573f50ddb4bce
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2371
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/build.go b/cue/build.go
index 7d775c3..ae23393 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -49,6 +49,7 @@
return nil, err
}
inst := idx.loadInstance(p)
+ inst.ImportPath = p.ImportPath
if inst.Err != nil {
return nil, inst.Err
}
@@ -121,7 +122,8 @@
labelMap map[string]label
labels []string
- loaded map[*build.Instance]*Instance
+ loaded map[*build.Instance]*Instance
+ imports map[value]*Instance // key is always a *structLit
offset label
parent *index
@@ -143,6 +145,7 @@
i := &index{
labelMap: map[string]label{"": 0},
labels: []string{""},
+ imports: map[value]*Instance{},
}
return i
}
@@ -153,6 +156,7 @@
i := &index{
labelMap: map[string]label{},
loaded: map[*build.Instance]*Instance{},
+ imports: map[value]*Instance{},
offset: label(len(parent.labels)) + parent.offset,
parent: parent,
}
@@ -228,6 +232,8 @@
inst.insertFile(f)
}
}
+ inst.ImportPath = p.ImportPath
+
inst.complete = true
return inst
}
diff --git a/cue/builtin.go b/cue/builtin.go
index 8a251cc..95282ef 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -251,6 +251,13 @@
e := mustCompileBuiltins(ctx, b, k)
builtins[k] = e
builtins["-/"+path.Base(k)] = e
+
+ sharedIndex.addInst(&Instance{
+ ImportPath: k,
+ Name: path.Base(k),
+ rootStruct: e,
+ rootValue: e,
+ })
}
}
diff --git a/cue/instance.go b/cue/instance.go
index d27db43..3a4eeec 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -48,15 +48,31 @@
complete bool // for cycle detection
}
+func (x *index) addInst(p *Instance) *Instance {
+ if p.rootStruct == nil {
+ panic("struct must not be nil")
+ }
+ p.index = x
+ x.imports[p.rootStruct] = p
+ return p
+}
+
+func (x *index) getImportFromNode(v value) *Instance {
+ imp := x.imports[v]
+ if imp == nil && x.parent != nil {
+ return x.parent.getImportFromNode(v)
+ }
+ return imp
+}
+
// NewInstance creates a new instance. Use Insert to populate the instance.
func (x *index) NewInstance(p *build.Instance) *Instance {
st := &structLit{baseValue: baseValue{nil}}
- i := &Instance{
- index: x,
+ i := x.addInst(&Instance{
rootStruct: st,
rootValue: st,
inst: p,
- }
+ })
if p != nil {
i.ImportPath = p.ImportPath
i.Dir = p.Dir
@@ -220,12 +236,11 @@
return nil
}
- p := &Instance{
+ p := ctx.index.addInst(&Instance{
rootStruct: st,
rootValue: merged,
- index: ctx.index,
complete: true,
- }
+ })
return p
}
@@ -305,18 +320,18 @@
case *bottom:
err = inst.Value().toErr(x)
}
- inst = &Instance{
+ inst = inst.index.addInst(&Instance{
rootStruct: st,
rootValue: st,
- index: inst.index,
inst: nil,
+ // TODO: somehow indicate this is not an original
ImportPath: inst.ImportPath,
Name: inst.Name,
Incomplete: inst.Incomplete,
Err: err,
complete: err != nil,
- }
+ })
return inst, err
}
diff --git a/cue/types.go b/cue/types.go
index 1b7615e..ee68d3b 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1163,41 +1163,53 @@
io.WriteString(state, debugStr(ctx, v.path.cache))
}
-// Reference returns path from the root of the file referred to by this value
-// or no path if this value is not a reference.
-func (v Value) Reference() []string {
+// Reference returns path from the root of the instance referred to by this
+// value such that inst.Lookup(path) resolves to the same value, or no path if
+// this value is not a reference,
+func (v Value) Reference() (inst *Instance, path []string) {
// TODO: don't include references to hidden fields.
if v.path == nil {
- return nil
+ return nil, nil
}
sel, ok := v.path.v.(*selectorExpr)
if !ok {
- return nil
+ return nil, nil
}
- return mkPath(v.ctx(), v.path, sel, 0)
+ imp, a := mkPath(v.ctx(), v.path, sel, 0)
+ return imp, a
}
-func mkPath(c *context, up *valueData, sel *selectorExpr, d int) (a []string) {
+func mkPath(c *context, up *valueData, sel *selectorExpr, d int) (imp *Instance, a []string) {
switch x := sel.x.(type) {
case *selectorExpr:
- a = mkPath(c, up.parent, x, d+1)
+ imp, a = mkPath(c, up.parent, x, d+1)
case *nodeRef:
// the parent must exist.
for ; up != nil && up.cache != x.node.(value); up = up.parent {
}
- a = mkFromRoot(c, up, d+1)
+ var v value
+ v, a = mkFromRoot(c, up, d+2)
+ if v == nil {
+ v = x.node
+ }
+ imp = c.getImportFromNode(x.node)
default:
panic("should not happend")
}
- return append(a, c.labelStr(sel.feature))
+ return imp, append(a, c.labelStr(sel.feature))
}
-func mkFromRoot(c *context, up *valueData, d int) []string {
- if up == nil || up.parent == nil {
- return make([]string, 0, d)
+func mkFromRoot(c *context, up *valueData, d int) (root value, a []string) {
+ if up == nil {
+ return nil, make([]string, 0, d)
}
- a := mkFromRoot(c, up.parent, d+1)
- return append(a, c.labelStr(up.feature))
+ root, a = mkFromRoot(c, up.parent, d+1)
+ if up.parent != nil {
+ a = append(a, c.labelStr(up.feature))
+ } else {
+ root = up.v
+ }
+ return root, a
}
// References reports all references used to evaluate this value. It does not
diff --git a/cue/types_test.go b/cue/types_test.go
index 8e819f5..c715819 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -1813,9 +1813,17 @@
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
v := getInstance(t, tc.input).Lookup("v")
- if got := strings.Join(v.Reference(), " "); got != tc.want {
+ inst, a := v.Reference()
+ if got := strings.Join(a, " "); got != tc.want {
t.Errorf("\n got %v;\nwant %v", got, tc.want)
}
+
+ if tc.want != "" {
+ v := inst.Lookup(a...)
+ if x, _ := v.Int64(); x != 1 {
+ t.Errorf("path resolved to %s; want 1", v)
+ }
+ }
})
}
}
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 3ba94c0..ff4c6a5 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -92,8 +92,8 @@
}
// shouldExpand reports is the given identifier is not exported.
-func (c *buildContext) shouldExpand(name string) bool {
- return c.expandRefs || c.isInternal(name)
+func (c *buildContext) shouldExpand(p *cue.Instance, ref []string) bool {
+ return c.expandRefs
}
func (b *builder) failf(v cue.Value, format string, args ...interface{}) {
@@ -127,16 +127,17 @@
count := 0
disallowDefault := false
var values cue.Value
- if b.ctx.shouldExpand(strings.Join(v.Reference(), ".")) {
+ p, a := v.Reference()
+ if b.ctx.shouldExpand(p, a) {
values = v
count = 1
} else {
for _, v := range appendSplit(nil, cue.AndOp, v) {
// This may be a reference to an enum. So we need to check references before
// dissecting them.
- switch r := v.Reference(); {
- case r != nil:
- b.addRef(r)
+ switch p, r := v.Reference(); {
+ case len(r) > 0:
+ b.addRef(v, p, r)
disallowDefault = true
default:
count++
@@ -727,9 +728,7 @@
b.add(c.finish())
}
-func (b *builder) addRef(ref []string) {
- // TODO: validate path.
- // TODO: map CUE types to OAPI types.
+func (b *builder) addRef(v cue.Value, inst *cue.Instance, ref []string) {
b.addConjunct(func(b *builder) {
a := append([]string{"#", b.ctx.refPrefix}, ref...)
b.set("$ref", path.Join(a...))