cmd/cue/cmd: report error for certain deprecated syntax

- block comments
- old-style comprehensions

Change-Id: I9ccc20b17d137a13d980c2c5b8663d41eac1bdde
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3874
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index 2cec1e8..af5d6b5 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -22,14 +22,31 @@
 	"testing"
 
 	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/build"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/load"
+	"cuelang.org/go/cue/parser"
 	"github.com/spf13/cobra"
 	"golang.org/x/text/language"
 	"golang.org/x/text/message"
 )
 
+// Disallow
+// - block comments
+// - old-style field comprehensions
+const syntaxVersion = -1000 + 12
+
+var defaultConfig = &load.Config{
+	Context: build.NewContext(
+		build.ParseFile(func(name string, src interface{}) (*ast.File, error) {
+			return parser.ParseFile(name, src,
+				parser.FromVersion(syntaxVersion),
+				parser.ParseComments,
+			)
+		})),
+}
+
 var runtime = &cue.Runtime{}
 
 var inTest = false
@@ -81,7 +98,7 @@
 }
 
 func buildFromArgs(cmd *Command, args []string) []*cue.Instance {
-	binst := loadFromArgs(cmd, args, nil)
+	binst := loadFromArgs(cmd, args, defaultConfig)
 	if binst == nil {
 		return nil
 	}
@@ -97,6 +114,9 @@
 }
 
 func buildInstances(cmd *Command, binst []*build.Instance) []*cue.Instance {
+	// TODO:
+	// If there are no files and User is true, then use those?
+	// Always use all files in user mode?
 	instances := cue.Build(binst)
 	for _, inst := range instances {
 		// TODO: consider merging errors of multiple files, but ensure
diff --git a/cmd/cue/cmd/trim.go b/cmd/cue/cmd/trim.go
index 13aaff6..52d0ec1 100644
--- a/cmd/cue/cmd/trim.go
+++ b/cmd/cue/cmd/trim.go
@@ -104,7 +104,7 @@
 	internal.DropOptional = true
 	defer func() { internal.DropOptional = false }()
 
-	binst := loadFromArgs(cmd, args, nil)
+	binst := loadFromArgs(cmd, args, defaultConfig)
 	if binst == nil {
 		return nil
 	}
diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go
index 9d2913d..2cc663e 100644
--- a/cmd/cue/cmd/vet.go
+++ b/cmd/cue/cmd/vet.go
@@ -85,7 +85,7 @@
 }
 
 func doVet(cmd *Command, args []string) error {
-	builds := loadFromArgs(cmd, args, nil)
+	builds := loadFromArgs(cmd, args, defaultConfig)
 	if builds == nil {
 		return nil
 	}
diff --git a/cue/build/context.go b/cue/build/context.go
index fc3a4c4..94cc2a5 100644
--- a/cue/build/context.go
+++ b/cue/build/context.go
@@ -27,13 +27,16 @@
 
 import (
 	"context"
+
+	"cuelang.org/go/cue/ast"
 )
 
 // A Context keeps track of state of building instances and caches work.
 type Context struct {
 	ctxt context.Context
 
-	loader LoadFunc
+	loader    LoadFunc
+	parseFunc func(str string, src interface{}) (*ast.File, error)
 
 	initialized bool
 
@@ -108,3 +111,19 @@
 func Loader(f LoadFunc) Option {
 	return func(c *Context) { c.loader = f }
 }
+
+// ParseFile is called to read and parse each file
+// when building syntax tree.
+// It must be safe to call ParseFile simultaneously from multiple goroutines.
+// If ParseFile is nil, the loader will uses parser.ParseFile.
+//
+// ParseFile should parse the source from src and use filename only for
+// recording position information.
+//
+// An application may supply a custom implementation of ParseFile
+// to change the effective file contents or the behavior of the parser,
+// or to modify the syntax tree. For example, changing the backwards
+// compatibility.
+func ParseFile(f func(filename string, src interface{}) (*ast.File, error)) Option {
+	return func(c *Context) { c.parseFunc = f }
+}
diff --git a/cue/build/instance.go b/cue/build/instance.go
index b8a792c..0011dfc 100644
--- a/cue/build/instance.go
+++ b/cue/build/instance.go
@@ -165,6 +165,13 @@
 	return inst.ctxt
 }
 
+func (inst *Instance) parse(name string, src interface{}) (*ast.File, error) {
+	if inst.ctxt != nil && inst.ctxt.parseFunc != nil {
+		return inst.ctxt.parseFunc(name, src)
+	}
+	return parser.ParseFile(name, src, parser.ParseComments)
+}
+
 // LookupImport defines a mapping from an ImportSpec's ImportPath to Instance.
 func (inst *Instance) LookupImport(path string) *Instance {
 	path = inst.expandPath(path)
@@ -193,7 +200,7 @@
 // It does not process the file's imports. The package name of the file must
 // match the package name of the instance.
 func (inst *Instance) AddFile(filename string, src interface{}) error {
-	file, err := parser.ParseFile(filename, src, parser.ParseComments)
+	file, err := inst.parse(filename, src)
 	if err != nil {
 		// should always be an errors.List, but just in case.
 		err := errors.Promote(err, "error adding file")