tools/trim: don't trim shared nodes
This one is a bit tricky. We accomplish this by explicitly marking
all nodes that cannot be removed. This seems like a good general
safety measure to make anyway, albeit a bit expensive.
Fixes #760
Change-Id: Ie46e398dd5295cedf150c11f38cdf82d68306180
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8702
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/tools/trim/testdata/shared.txtar b/tools/trim/testdata/shared.txtar
new file mode 100644
index 0000000..1aef0f1
--- /dev/null
+++ b/tools/trim/testdata/shared.txtar
@@ -0,0 +1,76 @@
+// Removing fields from shared nodes not supported.
+-- in.cue --
+import "strings"
+
+// TODO: this would be okay to normalize. Consider whether
+// this makes sense, though.
+a: {
+ service: [ID=_]: name: "a"
+
+ service: foo: shared
+ service: bar: shared
+
+ shared: name: "a"
+}
+
+issue760: {
+
+ service: [ID=_]: {
+ name: ID
+ }
+ service: foo: _shared
+ service: bar: _shared
+ _shared: {
+ name: "foo" // Do not remove!
+ }
+}
+
+
+issue760: {
+ service: [ID=_]: {
+ _service_name: *strings.TrimSuffix(ID, "-suffix") | string
+ }
+ service: "a-suffix": _shared
+ service: "b-suffix": _shared
+ _shared: {
+ _service_name: "a" // Do not remove!
+ }
+}
+
+-- out/trim --
+== in.cue
+import "strings"
+
+// TODO: this would be okay to normalize. Consider whether
+// this makes sense, though.
+a: {
+ service: [ID=_]: name: "a"
+
+ service: foo: shared
+ service: bar: shared
+
+ shared: name: "a"
+}
+
+issue760: {
+
+ service: [ID=_]: {
+ name: ID
+ }
+ service: foo: _shared
+ service: bar: _shared
+ _shared: {
+ name: "foo" // Do not remove!
+ }
+}
+
+issue760: {
+ service: [ID=_]: {
+ _service_name: *strings.TrimSuffix(ID, "-suffix") | string
+ }
+ service: "a-suffix": _shared
+ service: "b-suffix": _shared
+ _shared: {
+ _service_name: "a" // Do not remove!
+ }
+}
diff --git a/tools/trim/trim.go b/tools/trim/trim.go
index 5c4100f..7058259 100644
--- a/tools/trim/trim.go
+++ b/tools/trim/trim.go
@@ -78,11 +78,12 @@
v := vx.(*adt.Vertex)
t := &trimmer{
- Config: *cfg,
- ctx: adt.NewContext(r, v),
- remove: map[ast.Node]bool{},
- debug: Debug,
- w: os.Stderr,
+ Config: *cfg,
+ ctx: adt.NewContext(r, v),
+ remove: map[ast.Node]bool{},
+ exclude: map[ast.Node]bool{},
+ debug: Debug,
+ w: os.Stderr,
}
t.findSubordinates(v)
@@ -90,7 +91,7 @@
// Remove subordinate values from files.
for _, f := range files {
astutil.Apply(f, func(c astutil.Cursor) bool {
- if f, ok := c.Node().(*ast.Field); ok && t.remove[f.Value] {
+ if f, ok := c.Node().(*ast.Field); ok && t.remove[f.Value] && !t.exclude[f.Value] {
c.Delete()
}
return true
@@ -106,8 +107,9 @@
type trimmer struct {
Config
- ctx *adt.OpContext
- remove map[ast.Node]bool
+ ctx *adt.OpContext
+ remove map[ast.Node]bool
+ exclude map[ast.Node]bool
debug bool
indent int
@@ -125,6 +127,18 @@
}
}
+func (t *trimmer) markKeep(c adt.Conjunct) {
+ if isDominator(c) {
+ return
+ }
+ if src := c.Expr().Source(); src != nil {
+ t.exclude[src] = true
+ if t.debug {
+ t.logf("keeping")
+ }
+ }
+}
+
const dominatorNode = adt.ComprehensionSpan | adt.DefinitionSpan | adt.ConstraintSpan
// isDominator reports whether a node can remove other nodes.
@@ -135,17 +149,18 @@
// Removable reports whether a non-dominator conjunct can be removed. This is
// not the case if it has pattern constraints that could turn into dominator
// nodes.
-func removable(c adt.Conjunct) bool {
+func removable(c adt.Conjunct, v *adt.Vertex) bool {
return c.CloseInfo.FieldTypes&(adt.HasAdditional|adt.HasPattern) == 0
}
// Roots of constraints are not allowed to strip conjuncts by
// themselves as it will eliminate the reason for the trigger.
-func allowRemove(v *adt.Vertex) bool {
+func (t *trimmer) allowRemove(v *adt.Vertex) bool {
for _, c := range v.Conjuncts {
- if isDominator(c) &&
- (c.CloseInfo.Location() != c.Expr() ||
- c.CloseInfo.RootSpanType() != adt.ConstraintSpan) {
+ isDom := isDominator(c)
+ loc := c.CloseInfo.Location() != c.Expr()
+ isSpan := c.CloseInfo.RootSpanType() != adt.ConstraintSpan
+ if isDom && (loc || isSpan) {
return true
}
}
@@ -162,8 +177,15 @@
yes
)
-func (t *trimmer) findSubordinates(v *adt.Vertex) int {
+func (t *trimmer) findSubordinates(v *adt.Vertex) (result int) {
defer un(t.trace(v))
+ defer func() {
+ if result == no {
+ for _, c := range v.Conjuncts {
+ t.markKeep(c)
+ }
+ }
+ }()
// TODO(structure sharing): do not descend into vertices whose parent is not
// equal to the parent. This is not relevant at this time, but may be so in
@@ -184,7 +206,7 @@
}
}
- if !allowRemove(v) {
+ if !t.allowRemove(v) {
return no
}
@@ -228,7 +250,7 @@
}
for _, c := range v.Conjuncts {
- if !isDominator(c) && removable(c) {
+ if !isDominator(c) && removable(c, v) {
t.markRemove(c)
}
}