internal/core/adt: add more error positions

Fixes #681

Change-Id: If5242b68f57031a8c52f37978b5d9ae6b35aa03d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8785
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cmd/cue/cmd/testdata/script/vet_data.txt b/cmd/cue/cmd/testdata/script/vet_data.txt
index 7cde3c5..e9f202a 100644
--- a/cmd/cue/cmd/testdata/script/vet_data.txt
+++ b/cmd/cue/cmd/testdata/script/vet_data.txt
@@ -23,6 +23,8 @@
 -- vet-stderr --
 languages.1.name: invalid value "dutch" (out of bound =~"^\\p{Lu}"):
     ./schema.cue:3:8
+    ./data.yaml:5:12
 -- export-stderr --
 languages.1.name: invalid value "dutch" (out of bound =~"^\\p{Lu}"):
     ./schema.cue:3:8
+    ./data.yaml:5:12
diff --git a/cmd/cue/cmd/testdata/script/vet_path.txt b/cmd/cue/cmd/testdata/script/vet_path.txt
index c172451..64bd722 100644
--- a/cmd/cue/cmd/testdata/script/vet_path.txt
+++ b/cmd/cue/cmd/testdata/script/vet_path.txt
@@ -3,8 +3,10 @@
 -- expect-stderr --
 deployment.Booster.name: invalid value "Booster" (out of bound !~"^[A-Z]"):
     ./services.cue:1:29
+    ./services.jsonl:3:13
 service."Supplement\nfoo".name: invalid value "Supplement\nfoo" (out of bound !~"^[A-Z]"):
     ./services.cue:2:26
+    ./services.jsonl:3:13
 -- services.cue --
 deployment: [string]: name: !~"^[A-Z]"
 service: [string]: name: !~"^[A-Z]"
diff --git a/cue/testdata/basicrewrite/001_regexp.txtar b/cue/testdata/basicrewrite/001_regexp.txtar
index fe56fa7..0381a5f 100644
--- a/cue/testdata/basicrewrite/001_regexp.txtar
+++ b/cue/testdata/basicrewrite/001_regexp.txtar
@@ -71,6 +71,7 @@
     ./in.cue:22:13
 b3: invalid value "foo" (out of bound =~"[a-z]{4}"):
     ./in.cue:10:5
+    ./in.cue:11:5
 e1: cannot use 1 (type int) as type (string|bytes):
     ./in.cue:20:5
 e2: cannot use true (type bool) as type (string|bytes):
@@ -88,6 +89,7 @@
   b3: (_|_){
     // [eval] b3: invalid value "foo" (out of bound =~"[a-z]{4}"):
     //     ./in.cue:10:5
+    //     ./in.cue:11:5
   }
   b4: (string){ "foo" }
   s1: (string){ =~"c" }
diff --git a/cue/testdata/fulleval/035_optionals_with_label_filters.txtar b/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
index aae6258..c32a0a8 100644
--- a/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
+++ b/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
@@ -117,6 +117,7 @@
     ./in.cue:23:8
 jobs2.fooTest.name: invalid value "badName" (out of bound =~"^test"):
     ./in.cue:9:22
+    ./in.cue:19:23
 
 Result:
 (_|_){
@@ -151,6 +152,7 @@
       name: (_|_){
         // [eval] jobs2.fooTest.name: invalid value "badName" (out of bound =~"^test"):
         //     ./in.cue:9:22
+        //     ./in.cue:19:23
       }
       cmd: (string){ string }
     }
diff --git a/cue/testdata/resolve/013_custom_validators.txtar b/cue/testdata/resolve/013_custom_validators.txtar
index aade1ce..ae7c1d2 100644
--- a/cue/testdata/resolve/013_custom_validators.txtar
+++ b/cue/testdata/resolve/013_custom_validators.txtar
@@ -24,6 +24,7 @@
 b: invalid value "dog" (does not satisfy strings.ContainsAny("c")):
     ./in.cue:6:4
     ./in.cue:6:24
+    ./in.cue:7:4
 
 Result:
 (_|_){
@@ -33,6 +34,7 @@
     // [eval] b: invalid value "dog" (does not satisfy strings.ContainsAny("c")):
     //     ./in.cue:6:4
     //     ./in.cue:6:24
+    //     ./in.cue:7:4
   }
   c: (string){ "dog" }
 }
diff --git a/cue/testdata/resolve/048_builtins.txtar b/cue/testdata/resolve/048_builtins.txtar
index 70466cc..12cf475 100644
--- a/cue/testdata/resolve/048_builtins.txtar
+++ b/cue/testdata/resolve/048_builtins.txtar
@@ -95,8 +95,10 @@
     ./in.cue:20:24
 a3.a: invalid value "bar" (out of bound =~"oo"):
     ./in.cue:8:5
+    ./in.cue:12:14
 a3.a: invalid value "bar" (out of bound =~"fo"):
     ./in.cue:9:5
+    ./in.cue:12:14
 stringListErrors.b.result: invalid type element 0 (int) of string list argument 0:
     ./in.cue:31:17
 stringListErrors.c.result: cannot use int (type int) as string in argument 2 to strings.Join:
@@ -122,8 +124,10 @@
     a: (_|_){
       // [eval] a3.a: invalid value "bar" (out of bound =~"oo"):
       //     ./in.cue:8:5
+      //     ./in.cue:12:14
       // a3.a: invalid value "bar" (out of bound =~"fo"):
       //     ./in.cue:9:5
+      //     ./in.cue:12:14
     }
     b: (string){ =~"oo" }
     c: (string){ =~"fo" }
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index f128606..e1d96df 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -643,7 +643,14 @@
 	return arc, isNew
 }
 
-func (v *Vertex) Source() ast.Node { return nil }
+func (v *Vertex) Source() ast.Node {
+	if v != nil {
+		if b, ok := v.BaseValue.(Value); ok {
+			return b.Source()
+		}
+	}
+	return nil
+}
 
 // AddConjunct adds the given Conjuncts to v if it doesn't already exist.
 func (v *Vertex) AddConjunct(c Conjunct) *Bottom {
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 3e46997..ed1f911 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -564,6 +564,7 @@
 		}
 	}
 	if v.Concreteness() > Concrete {
+		// TODO(errors): analyze dependencies of x.Expr to get positions.
 		ctx.addErrf(IncompleteError, ctx.pos(),
 			"non-concrete value %s for bound %s", ctx.Str(x.Expr), x.Op)
 		return nil
@@ -615,8 +616,10 @@
 		}
 		// TODO(errors): use "invalid value %v (not an %s)" if x is a
 		// predeclared identifier such as `int`.
-		return c.NewErrf("invalid value %v (out of bound %s)",
+		err := c.Newf("invalid value %v (out of bound %s)",
 			c.Str(y), c.Str(x))
+		err.AddPosition(y)
+		return &Bottom{Src: c.src, Err: err, Code: EvalError}
 
 	default:
 		panic(fmt.Sprintf("unsupported type %T", v))
diff --git a/pkg/net/testdata/gen.txtar b/pkg/net/testdata/gen.txtar
index 1f124a3..5e06f72 100644
--- a/pkg/net/testdata/gen.txtar
+++ b/pkg/net/testdata/gen.txtar
@@ -23,9 +23,11 @@
 -- out/net --
 Errors:
 error in call to net.JoinHostPort: invalid host [192, 30, 4]
-t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4)
+t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4):
+    ./in.cue:11:16
 t13: invalid value "ff02::1:3" (does not satisfy net.IPv4):
     ./in.cue:15:6
+    ./in.cue:15:19
 
 Result:
 t1: "foo.bar."
diff --git a/pkg/regexp/testdata/gen.txtar b/pkg/regexp/testdata/gen.txtar
index c0f213d..058d1c3 100644
--- a/pkg/regexp/testdata/gen.txtar
+++ b/pkg/regexp/testdata/gen.txtar
@@ -23,7 +23,8 @@
 error in call to regexp.FindAll: no match
 error in call to regexp.FindAllNamedSubmatch: no match
 error in call to regexp.FindAllSubmatch: no match
-t14: invalid value "invalid)" (does not satisfy regexp.Valid): error in call to regexp.Valid: error parsing regexp: unexpected ): `invalid)`
+t14: invalid value "invalid)" (does not satisfy regexp.Valid): error in call to regexp.Valid: error parsing regexp: unexpected ): `invalid)`:
+    ./in.cue:16:21
 
 Result:
 t1: "foo"
diff --git a/pkg/strings/testdata/gen.txtar b/pkg/strings/testdata/gen.txtar
index 5977669..b30bc7b 100644
--- a/pkg/strings/testdata/gen.txtar
+++ b/pkg/strings/testdata/gen.txtar
@@ -27,15 +27,19 @@
 t10: invalid value "quux" (does not satisfy strings.MaxRunes(3)):
     ./in.cue:12:6
     ./in.cue:12:23
+    ./in.cue:12:28
 t12: invalid value "e" (does not satisfy strings.MaxRunes(0)):
     ./in.cue:14:6
     ./in.cue:14:23
+    ./in.cue:14:28
 t16: invalid value "hello" (does not satisfy strings.MaxRunes(3)):
     ./in.cue:18:6
     ./in.cue:18:23
+    ./in.cue:18:28
 t17: invalid value "hello" (does not satisfy strings.MinRunes(10)):
     ./in.cue:19:6
     ./in.cue:19:23
+    ./in.cue:19:29
 
 Result:
 t1: "Hello World!"
diff --git a/pkg/time/testdata/gen.txtar b/pkg/time/testdata/gen.txtar
index 235744d..6928190 100644
--- a/pkg/time/testdata/gen.txtar
+++ b/pkg/time/testdata/gen.txtar
@@ -8,7 +8,8 @@
 t3: time.Unix(1500000000, 123456)
 -- out/time --
 Errors:
-t2: invalid value "no time" (does not satisfy time.Time): error in call to time.Time: invalid time "no time"
+t2: invalid value "no time" (does not satisfy time.Time): error in call to time.Time: invalid time "no time":
+    ./in.cue:4:17
 
 Result:
 t1: "1937-01-01T12:00:27.87+00:20"