cmd/cue/cmd: ignore optional fields for trim

There are rare cases where trim might remove an
optional field, but optional fields are more likely
to interfere with the removal logic.

Change-Id: Iab5b680cee9dbf0e0f4b06979ef43d2f0a5c6001
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1804
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/trim.go b/cmd/cue/cmd/trim.go
index 1da1dce..ae64826 100644
--- a/cmd/cue/cmd/trim.go
+++ b/cmd/cue/cmd/trim.go
@@ -100,6 +100,16 @@
 )
 
 func runTrim(cmd *cobra.Command, args []string) error {
+	// TODO: Do something more fine-grained. Optional fields are mostly not
+	// useful to consider as an optional field will almost never subsume
+	// another value. However, an optional field may subsume and therefore
+	// trigger the removal of another optional field.
+	// For now this is the better approach: trimming is not 100% accurate,
+	// and optional fields are just more likely to cause edge cases that may
+	// block a removal.
+	internal.DropOptional = true
+	defer func() { internal.DropOptional = false }()
+
 	log.SetOutput(cmd.OutOrStderr())
 
 	ctxt := build.NewContext(build.ParseOptions(parser.ParseComments))
@@ -352,7 +362,7 @@
 
 		// Build map of mixin fields.
 		valueMap := map[key]cue.Value{}
-		for mIter, _ := in.Fields(cue.All()); mIter.Next(); {
+		for mIter, _ := in.Fields(cue.All(), cue.Optional(false)); mIter.Next(); {
 			valueMap[iterKey(mIter)] = mIter.Value()
 		}
 
diff --git a/cue/ast.go b/cue/ast.go
index c4a2366..5246b30 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -22,6 +22,7 @@
 	"cuelang.org/go/cue/build"
 	"cuelang.org/go/cue/literal"
 	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
 )
 
 // insertFile inserts the given file at the root of the instance.
@@ -285,6 +286,9 @@
 			}
 
 		case *ast.BasicLit, *ast.Ident:
+			if internal.DropOptional && opt {
+				break
+			}
 			attrs, err := createAttrs(v.ctx(), newNode(n), n.Attrs)
 			if err != nil {
 				return err
diff --git a/internal/internal.go b/internal/internal.go
index f9ff342..88b4b76 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -39,3 +39,7 @@
 // instance must be of type *cue.Instance.
 // The returned value is a cue.Value, which the caller must cast to.
 var FromGoType func(instance, x interface{}) interface{}
+
+// DropOptional is a blanket override of handling optional values during
+// compilation. TODO: should we make this a build option?
+var DropOptional bool