internal/filetpes: improve error messages

Fixes #384

Change-Id: I0cf677ccc1aa4e285cb7600b10235edd7d5e7ed6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7803
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cmd/cue/cmd/script_test.go b/cmd/cue/cmd/script_test.go
index 71671be..b11555b 100644
--- a/cmd/cue/cmd/script_test.go
+++ b/cmd/cue/cmd/script_test.go
@@ -84,7 +84,7 @@
 // Usage Comment out t.Skip() and set file to test.
 func TestX(t *testing.T) {
 	t.Skip()
-	const path = "./testdata/script/eval_context.txt"
+	const path = "./testdata/script/eval_e.txt"
 
 	check := func(err error) {
 		t.Helper()
diff --git a/cmd/cue/cmd/testdata/script/eval_e.txt b/cmd/cue/cmd/testdata/script/eval_e.txt
index 7844b7d..7d36f10 100644
--- a/cmd/cue/cmd/testdata/script/eval_e.txt
+++ b/cmd/cue/cmd/testdata/script/eval_e.txt
@@ -7,6 +7,9 @@
 cmp stderr expect/incomplete/stderr
 cmp stdout expect/incomplete/stdout
 
+# Issue #384
+! cue eval foo.bar
+cmp stderr expect/foobar/stderr
 
 -- expect/nonExist/stdout --
 -- expect/nonExist/stderr --
@@ -15,6 +18,11 @@
 -- expect/incomplete/stdout --
 
 -- expect/incomplete/stderr --
+-- expect/foobar/stdout --
+
+-- expect/foobar/stderr --
+unknown file extension .bar
+-- input/ --
 -- partial.cue --
 package exitcode
 
@@ -26,6 +34,8 @@
 
 incomplete: pkg.Settings
 
+foo: bar: "hello"
+
 -- cue.mod/pkg/foo.com/example/example.cue --
 package example
 
diff --git a/internal/filetypes/filetypes.go b/internal/filetypes/filetypes.go
index 2498767..451b16f 100644
--- a/internal/filetypes/filetypes.go
+++ b/internal/filetypes/filetypes.go
@@ -97,7 +97,7 @@
 	}
 
 	i := cuegenInstance.Value()
-	i = i.Unify(i.Lookup("modes", mode.String()))
+	i, errs := update(nil, i, i, "modes", mode.String())
 	v := i.LookupDef("FileInfo")
 	v = v.Fill(b)
 
@@ -110,25 +110,34 @@
 
 	interpretation, _ := v.Lookup("interpretation").String()
 	if b.Form != "" {
-		v = v.Unify(i.Lookup("forms", string(b.Form)))
+		v, errs = update(errs, v, i, "forms", string(b.Form))
 		// may leave some encoding-dependent options open in data mode.
 	} else if interpretation != "" {
 		// always sets schema form.
-		v = v.Unify(i.Lookup("interpretations", interpretation))
+		v, errs = update(errs, v, i, "interpretations", interpretation)
 	}
 	if interpretation == "" {
 		s, err := v.Lookup("encoding").String()
 		if err != nil {
 			return nil, err
 		}
-		v = v.Unify(i.Lookup("encodings", s))
+		v, errs = update(errs, v, i, "encodings", s)
 	}
 
 	fi := &FileInfo{}
 	if err := v.Decode(fi); err != nil {
-		return nil, err
+		return nil, errors.Wrapf(err, token.NoPos, "could not parse arguments")
 	}
-	return fi, nil
+	return fi, errs
+}
+
+func update(errs errors.Error, v, i cue.Value, field, value string) (cue.Value, errors.Error) {
+	v = v.Unify(i.Lookup(field, value))
+	if err := v.Err(); err != nil {
+		errs = errors.Append(errs,
+			errors.Newf(token.NoPos, "unknown %s %s", field, value))
+	}
+	return v, errs
 }
 
 // ParseArgs converts a sequence of command line arguments representing
@@ -245,6 +254,10 @@
 		} else if ext := filepath.Ext(filename); ext != "" {
 			if x := i.Lookup("extensions", ext); x.Exists() || !hasDefault {
 				v = v.Unify(x)
+				if err := v.Err(); err != nil {
+					return nil, errors.Newf(token.NoPos,
+						"unknown file extension %s", ext)
+				}
 			}
 		} else if !hasDefault {
 			return nil, errors.Newf(token.NoPos,
@@ -254,7 +267,8 @@
 
 	f := &build.File{}
 	if err := v.Decode(&f); err != nil {
-		return nil, err
+		return nil, errors.Wrapf(err, token.NoPos,
+			"could not determine file type")
 	}
 	return f, nil
 }