go/types: add unexported start and end positions to type checker errors

Tools often need to associate errors not with a single position, but
with a span of source code. For example, gopls currently estimates
diagnostic spans using heuristics to expand the positions reported by
the type checker to surrounding source code. Unfortunately this is often
inaccurate.

This CL lays the groundwork to solve this within go/types by adding a
start and end position to type checker errors. This is an experimental
API, both because we are uncertain of the ideal representation for these
spans and because their initial positioning is naive. In most cases this
CL simply expands errors to the surrounding ast.Node being typechecked,
if available. This might not be the best error span to present to the
user. For these reasons the API is unexported -- gopls can read these
positions using reflection, allowing us to gain experience and improve
them during the next development cycle.

For golang/go#42290

Change-Id: I39a04d70ea2bb2134b4d4c937f32b2ddb4456430
Reviewed-on: https://go-review.googlesource.com/c/go/+/265250
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
diff --git a/src/go/types/api.go b/src/go/types/api.go
index abe1f9f..d625959 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -49,7 +49,9 @@
 	// to preview this feature may read go116code using reflection (see
 	// errorcodes_test.go), but beware that there is no guarantee of future
 	// compatibility.
-	go116code errorCode
+	go116code  errorCode
+	go116start token.Pos
+	go116end   token.Pos
 }
 
 // Error returns an error string formatted as follows:
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index d895c6f..616564b 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -38,7 +38,7 @@
 		// complex, or string constant."
 		if T == nil || IsInterface(T) {
 			if T == nil && x.typ == Typ[UntypedNil] {
-				check.errorf(x.pos(), _UntypedNil, "use of untyped nil in %s", context)
+				check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
 				x.mode = invalid
 				return
 			}
@@ -59,7 +59,7 @@
 					code = ierr.go116code
 				}
 			}
-			check.error(x.pos(), code, msg)
+			check.error(x, code, msg)
 			x.mode = invalid
 			return
 		}
@@ -76,9 +76,9 @@
 	reason := ""
 	if ok, code := x.assignableTo(check, T, &reason); !ok {
 		if reason != "" {
-			check.errorf(x.pos(), code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
+			check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
 		} else {
-			check.errorf(x.pos(), code, "cannot use %s as %s value in %s", x, T, context)
+			check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context)
 		}
 		x.mode = invalid
 	}
@@ -94,7 +94,7 @@
 
 	// rhs must be a constant
 	if x.mode != constant_ {
-		check.errorf(x.pos(), _InvalidConstInit, "%s is not constant", x)
+		check.errorf(x, _InvalidConstInit, "%s is not constant", x)
 		if lhs.typ == nil {
 			lhs.typ = Typ[Invalid]
 		}
@@ -129,7 +129,7 @@
 		if isUntyped(typ) {
 			// convert untyped types to default types
 			if typ == Typ[UntypedNil] {
-				check.errorf(x.pos(), _UntypedNil, "use of untyped nil in %s", context)
+				check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
 				lhs.typ = Typ[Invalid]
 				return nil
 			}
@@ -203,11 +203,11 @@
 			var op operand
 			check.expr(&op, sel.X)
 			if op.mode == mapindex {
-				check.errorf(z.pos(), _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr))
+				check.errorf(&z, _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr))
 				return nil
 			}
 		}
-		check.errorf(z.pos(), _UnassignableOperand, "cannot assign to %s", &z)
+		check.errorf(&z, _UnassignableOperand, "cannot assign to %s", &z)
 		return nil
 	}
 
@@ -236,10 +236,10 @@
 		}
 		check.useGetter(get, r)
 		if returnPos.IsValid() {
-			check.errorf(returnPos, _WrongResultCount, "wrong number of return values (want %d, got %d)", l, r)
+			check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", l, r)
 			return
 		}
-		check.errorf(rhs[0].Pos(), _WrongAssignCount, "cannot initialize %d variables with %d values", l, r)
+		check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", l, r)
 		return
 	}
 
@@ -274,7 +274,7 @@
 	}
 	if l != r {
 		check.useGetter(get, r)
-		check.errorf(rhs[0].Pos(), _WrongAssignCount, "cannot assign %d values to %d variables", r, l)
+		check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", r, l)
 		return
 	}
 
@@ -295,7 +295,7 @@
 	}
 }
 
-func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
+func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
 	top := len(check.delayed)
 	scope := check.scope
 
@@ -315,7 +315,7 @@
 				if alt, _ := alt.(*Var); alt != nil {
 					obj = alt
 				} else {
-					check.errorf(lhs.Pos(), _UnassignableOperand, "cannot assign to %s", lhs)
+					check.errorf(lhs, _UnassignableOperand, "cannot assign to %s", lhs)
 				}
 				check.recordUse(ident, alt)
 			} else {
@@ -328,7 +328,7 @@
 			}
 		} else {
 			check.useLHS(lhs)
-			check.invalidAST(lhs.Pos(), "cannot declare %s", lhs)
+			check.invalidAST(lhs, "cannot declare %s", lhs)
 		}
 		if obj == nil {
 			obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index 960d1f2..fd35f78 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -21,7 +21,9 @@
 	// append is the only built-in that permits the use of ... for the last argument
 	bin := predeclaredFuncs[id]
 	if call.Ellipsis.IsValid() && id != _Append {
-		check.invalidOp(call.Ellipsis, _InvalidDotDotDot, "invalid use of ... with built-in %s", bin.name)
+		check.invalidOp(atPos(call.Ellipsis),
+			_InvalidDotDotDot,
+			"invalid use of ... with built-in %s", bin.name)
 		check.use(call.Args...)
 		return
 	}
@@ -68,7 +70,7 @@
 			msg = "too many"
 		}
 		if msg != "" {
-			check.invalidOp(call.Rparen, _WrongArgCount, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
+			check.invalidOp(inNode(call, call.Rparen), _WrongArgCount, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
 			return
 		}
 	}
@@ -85,7 +87,7 @@
 		if s, _ := S.Underlying().(*Slice); s != nil {
 			T = s.elem
 		} else {
-			check.invalidArg(x.pos(), _InvalidAppend, "%s is not a slice", x)
+			check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
 			return
 		}
 
@@ -181,7 +183,7 @@
 			if id == _Len {
 				code = _InvalidLen
 			}
-			check.invalidArg(x.pos(), code, "%s for %s", x, bin.name)
+			check.invalidArg(x, code, "%s for %s", x, bin.name)
 			return
 		}
 
@@ -196,11 +198,11 @@
 		// close(c)
 		c, _ := x.typ.Underlying().(*Chan)
 		if c == nil {
-			check.invalidArg(x.pos(), _InvalidClose, "%s is not a channel", x)
+			check.invalidArg(x, _InvalidClose, "%s is not a channel", x)
 			return
 		}
 		if c.dir == RecvOnly {
-			check.invalidArg(x.pos(), _InvalidClose, "%s must not be a receive-only channel", x)
+			check.invalidArg(x, _InvalidClose, "%s must not be a receive-only channel", x)
 			return
 		}
 
@@ -264,13 +266,13 @@
 
 		// both argument types must be identical
 		if !check.identical(x.typ, y.typ) {
-			check.invalidArg(x.pos(), _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ)
+			check.invalidArg(x, _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ)
 			return
 		}
 
 		// the argument types must be of floating-point type
 		if !isFloat(x.typ) {
-			check.invalidArg(x.pos(), _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
+			check.invalidArg(x, _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
 			return
 		}
 
@@ -324,12 +326,12 @@
 		}
 
 		if dst == nil || src == nil {
-			check.invalidArg(x.pos(), _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
+			check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
 			return
 		}
 
 		if !check.identical(dst, src) {
-			check.invalidArg(x.pos(), _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+			check.invalidArg(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
 			return
 		}
 
@@ -343,7 +345,7 @@
 		// delete(m, k)
 		m, _ := x.typ.Underlying().(*Map)
 		if m == nil {
-			check.invalidArg(x.pos(), _InvalidDelete, "%s is not a map", x)
+			check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
 			return
 		}
 		arg(x, 1) // k
@@ -352,7 +354,7 @@
 		}
 
 		if ok, code := x.assignableTo(check, m.key, nil); !ok {
-			check.invalidArg(x.pos(), code, "%s is not assignable to %s", x, m.key)
+			check.invalidArg(x, code, "%s is not assignable to %s", x, m.key)
 			return
 		}
 
@@ -392,7 +394,7 @@
 			if id == _Real {
 				code = _InvalidReal
 			}
-			check.invalidArg(x.pos(), code, "argument has type %s, expected complex type", x.typ)
+			check.invalidArg(x, code, "argument has type %s, expected complex type", x.typ)
 			return
 		}
 
@@ -444,11 +446,11 @@
 		case *Map, *Chan:
 			min = 1
 		default:
-			check.invalidArg(arg0.Pos(), _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
+			check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
 			return
 		}
 		if nargs < min || min+1 < nargs {
-			check.errorf(call.Pos(), _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
+			check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
 			return
 		}
 		types := []Type{T}
@@ -461,7 +463,7 @@
 			}
 		}
 		if len(sizes) == 2 && sizes[0] > sizes[1] {
-			check.invalidArg(call.Args[1].Pos(), _SwappedMakeArgs, "length and capacity swapped")
+			check.invalidArg(call.Args[1], _SwappedMakeArgs, "length and capacity swapped")
 			// safe to continue
 		}
 		x.mode = value
@@ -559,7 +561,7 @@
 		arg0 := call.Args[0]
 		selx, _ := unparen(arg0).(*ast.SelectorExpr)
 		if selx == nil {
-			check.invalidArg(arg0.Pos(), _BadOffsetofSyntax, "%s is not a selector expression", arg0)
+			check.invalidArg(arg0, _BadOffsetofSyntax, "%s is not a selector expression", arg0)
 			check.use(arg0)
 			return
 		}
@@ -574,18 +576,18 @@
 		obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
 		switch obj.(type) {
 		case nil:
-			check.invalidArg(x.pos(), _MissingFieldOrMethod, "%s has no single field %s", base, sel)
+			check.invalidArg(x, _MissingFieldOrMethod, "%s has no single field %s", base, sel)
 			return
 		case *Func:
 			// TODO(gri) Using derefStructPtr may result in methods being found
 			// that don't actually exist. An error either way, but the error
 			// message is confusing. See: https://play.golang.org/p/al75v23kUy ,
 			// but go/types reports: "invalid argument: x.m is a method value".
-			check.invalidArg(arg0.Pos(), _InvalidOffsetof, "%s is a method value", arg0)
+			check.invalidArg(arg0, _InvalidOffsetof, "%s is a method value", arg0)
 			return
 		}
 		if indirect {
-			check.invalidArg(x.pos(), _InvalidOffsetof, "field %s is embedded via a pointer in %s", sel, base)
+			check.invalidArg(x, _InvalidOffsetof, "field %s is embedded via a pointer in %s", sel, base)
 			return
 		}
 
@@ -615,15 +617,15 @@
 		// The result of assert is the value of pred if there is no error.
 		// Note: assert is only available in self-test mode.
 		if x.mode != constant_ || !isBoolean(x.typ) {
-			check.invalidArg(x.pos(), _Test, "%s is not a boolean constant", x)
+			check.invalidArg(x, _Test, "%s is not a boolean constant", x)
 			return
 		}
 		if x.val.Kind() != constant.Bool {
-			check.errorf(x.pos(), _Test, "internal error: value of %s should be a boolean constant", x)
+			check.errorf(x, _Test, "internal error: value of %s should be a boolean constant", x)
 			return
 		}
 		if !constant.BoolVal(x.val) {
-			check.errorf(call.Pos(), _Test, "%v failed", call)
+			check.errorf(call, _Test, "%v failed", call)
 			// compile-time assertion failure - safe to continue
 		}
 		// result is constant - no need to record signature
@@ -643,7 +645,7 @@
 		x1 := x
 		for _, arg := range call.Args {
 			check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
-			check.dump("%v: %s", x1.pos(), x1)
+			check.dump("%v: %s", x1.Pos(), x1)
 			x1 = &t // use incoming x only for first argument
 		}
 		// trace is only available in test mode - no need to record signature
diff --git a/src/go/types/call.go b/src/go/types/call.go
index fd0cfe3..992598d 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -29,7 +29,7 @@
 		x.mode = invalid
 		switch n := len(e.Args); n {
 		case 0:
-			check.errorf(e.Rparen, _WrongArgCount, "missing argument in conversion to %s", T)
+			check.errorf(inNode(e, e.Rparen), _WrongArgCount, "missing argument in conversion to %s", T)
 		case 1:
 			check.expr(x, e.Args[0])
 			if x.mode != invalid {
@@ -37,7 +37,7 @@
 			}
 		default:
 			check.use(e.Args...)
-			check.errorf(e.Args[n-1].Pos(), _WrongArgCount, "too many arguments in conversion to %s", T)
+			check.errorf(e.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
 		}
 		x.expr = e
 		return conversion
@@ -60,7 +60,7 @@
 
 		sig, _ := x.typ.Underlying().(*Signature)
 		if sig == nil {
-			check.invalidOp(x.pos(), _InvalidCall, "cannot call non-function %s", x)
+			check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
 			x.mode = invalid
 			x.expr = e
 			return statement
@@ -231,13 +231,13 @@
 	if call.Ellipsis.IsValid() {
 		// last argument is of the form x...
 		if !sig.variadic {
-			check.errorf(call.Ellipsis, _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun)
+			check.errorf(atPos(call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun)
 			check.useGetter(arg, n)
 			return
 		}
 		if len(call.Args) == 1 && n > 1 {
 			// f()... is not permitted if f() is multi-valued
-			check.errorf(call.Ellipsis, _InvalidDotDotDotOperand, "cannot use ... with %d-valued %s", n, call.Args[0])
+			check.errorf(atPos(call.Ellipsis), _InvalidDotDotDotOperand, "cannot use ... with %d-valued %s", n, call.Args[0])
 			check.useGetter(arg, n)
 			return
 		}
@@ -263,7 +263,7 @@
 		n++
 	}
 	if n < sig.params.Len() {
-		check.errorf(call.Rparen, _WrongArgCount, "too few arguments in call to %s", call.Fun)
+		check.errorf(inNode(call, call.Rparen), _WrongArgCount, "too few arguments in call to %s", call.Fun)
 		// ok to continue
 	}
 }
@@ -291,18 +291,18 @@
 			}
 		}
 	default:
-		check.errorf(x.pos(), _WrongArgCount, "too many arguments")
+		check.errorf(x, _WrongArgCount, "too many arguments")
 		return
 	}
 
 	if ellipsis.IsValid() {
 		if i != n-1 {
-			check.errorf(ellipsis, _MisplacedDotDotDot, "can only use ... with matching parameter")
+			check.errorf(atPos(ellipsis), _MisplacedDotDotDot, "can only use ... with matching parameter")
 			return
 		}
 		// argument is of the form x... and x is single-valued
 		if _, ok := x.typ.Underlying().(*Slice); !ok && x.typ != Typ[UntypedNil] { // see issue #18268
-			check.errorf(x.pos(), _InvalidDotDotDotOperand, "cannot use %s as parameter of type %s", x, typ)
+			check.errorf(x, _InvalidDotDotDotOperand, "cannot use %s as parameter of type %s", x, typ)
 			return
 		}
 	} else if sig.variadic && i >= n-1 {
@@ -365,7 +365,7 @@
 					}
 				}
 				if exp == nil {
-					check.errorf(e.Sel.Pos(), _UndeclaredImportedName, "%s not declared by package C", sel)
+					check.errorf(e.Sel, _UndeclaredImportedName, "%s not declared by package C", sel)
 					goto Error
 				}
 				check.objDecl(exp, nil)
@@ -373,12 +373,12 @@
 				exp = pkg.scope.Lookup(sel)
 				if exp == nil {
 					if !pkg.fake {
-						check.errorf(e.Sel.Pos(), _UndeclaredImportedName, "%s not declared by package %s", sel, pkg.name)
+						check.errorf(e.Sel, _UndeclaredImportedName, "%s not declared by package %s", sel, pkg.name)
 					}
 					goto Error
 				}
 				if !exp.Exported() {
-					check.errorf(e.Sel.Pos(), _UnexportedName, "%s not exported by package %s", sel, pkg.name)
+					check.errorf(e.Sel, _UnexportedName, "%s not exported by package %s", sel, pkg.name)
 					// ok to continue
 				}
 			}
@@ -431,9 +431,9 @@
 		switch {
 		case index != nil:
 			// TODO(gri) should provide actual type where the conflict happens
-			check.errorf(e.Sel.Pos(), _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel)
+			check.errorf(e.Sel, _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel)
 		case indirect:
-			check.errorf(e.Sel.Pos(), _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
+			check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
 		default:
 			// Check if capitalization of sel matters and provide better error
 			// message in that case.
@@ -445,11 +445,11 @@
 					changeCase = string(unicode.ToUpper(r)) + sel[1:]
 				}
 				if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
-					check.errorf(e.Sel.Pos(), _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s, but does have %s)", x.expr, sel, x.typ, sel, changeCase)
+					check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s, but does have %s)", x.expr, sel, x.typ, sel, changeCase)
 					break
 				}
 			}
-			check.errorf(e.Sel.Pos(), _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s)", x.expr, sel, x.typ, sel)
+			check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s)", x.expr, sel, x.typ, sel)
 		}
 		goto Error
 	}
@@ -464,7 +464,7 @@
 		m, _ := obj.(*Func)
 		if m == nil {
 			// TODO(gri) should check if capitalization of sel matters and provide better error message in that case
-			check.errorf(e.Sel.Pos(), _MissingFieldOrMethod, "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
+			check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
 			goto Error
 		}
 
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 407faa0..5e7bd92 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -85,8 +85,8 @@
 	// information collected during type-checking of a set of package files
 	// (initialized by Files, valid only for the duration of check.Files;
 	// maps and lists are allocated on demand)
-	files            []*ast.File                       // package files
-	unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
+	files            []*ast.File                             // package files
+	unusedDotImports map[*Scope]map[*Package]*ast.ImportSpec // unused dot-imported packages
 
 	firstErr error                 // first error encountered
 	methods  map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
@@ -105,18 +105,18 @@
 
 // addUnusedImport adds the position of a dot-imported package
 // pkg to the map of dot imports for the given file scope.
-func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
+func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, spec *ast.ImportSpec) {
 	mm := check.unusedDotImports
 	if mm == nil {
-		mm = make(map[*Scope]map[*Package]token.Pos)
+		mm = make(map[*Scope]map[*Package]*ast.ImportSpec)
 		check.unusedDotImports = mm
 	}
 	m := mm[scope]
 	if m == nil {
-		m = make(map[*Package]token.Pos)
+		m = make(map[*Package]*ast.ImportSpec)
 		mm[scope] = m
 	}
-	m[pkg] = pos
+	m[pkg] = spec
 }
 
 // addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
@@ -217,7 +217,7 @@
 			if name != "_" {
 				pkg.name = name
 			} else {
-				check.errorf(file.Name.Pos(), _BlankPkgName, "invalid package name _")
+				check.errorf(file.Name, _BlankPkgName, "invalid package name _")
 			}
 			fallthrough
 
@@ -225,7 +225,7 @@
 			check.files = append(check.files, file)
 
 		default:
-			check.errorf(file.Package, _MismatchedPkgName, "package %s; expected %s", name, pkg.name)
+			check.errorf(atPos(file.Package), _MismatchedPkgName, "package %s; expected %s", name, pkg.name)
 			// ignore this file
 		}
 	}
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index 4f47140..0955391 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -38,7 +38,7 @@
 	}
 
 	if !ok {
-		check.errorf(x.pos(), _InvalidConversion, "cannot convert %s to %s", x, T)
+		check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
 		x.mode = invalid
 		return
 	}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 416878d..17b66ca 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -15,7 +15,7 @@
 		// We use "other" rather than "previous" here because
 		// the first declaration seen may not be textually
 		// earlier in the source.
-		check.errorf(pos, _DuplicateDecl, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+		check.errorf(obj, _DuplicateDecl, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
 	}
 }
 
@@ -26,7 +26,7 @@
 	// binding."
 	if obj.Name() != "_" {
 		if alt := scope.Insert(obj); alt != nil {
-			check.errorf(obj.Pos(), _DuplicateDecl, "%s redeclared in this block", obj.Name())
+			check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
 			check.reportAltDecl(alt)
 			return
 		}
@@ -357,16 +357,16 @@
 	//           cycle? That would be more consistent with other error messages.
 	i := firstInSrc(cycle)
 	obj := cycle[i]
-	check.errorf(obj.Pos(), _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
+	check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
 	for range cycle {
-		check.errorf(obj.Pos(), _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
+		check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
 		i++
 		if i >= len(cycle) {
 			i = 0
 		}
 		obj = cycle[i]
 	}
-	check.errorf(obj.Pos(), _InvalidDeclCycle, "\t%s", obj.Name())
+	check.errorf(obj, _InvalidDeclCycle, "\t%s", obj.Name())
 }
 
 // firstInSrc reports the index of the object with the "smallest"
@@ -436,18 +436,18 @@
 					check.arityMatch(s, nil)
 					f(varDecl{s})
 				default:
-					check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+					check.invalidAST(s, "invalid token %s", d.Tok)
 				}
 			case *ast.TypeSpec:
 				f(typeDecl{s})
 			default:
-				check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+				check.invalidAST(s, "unknown ast.Spec node %T", s)
 			}
 		}
 	case *ast.FuncDecl:
 		f(funcDecl{d})
 	default:
-		check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+		check.invalidAST(d, "unknown ast.Decl node %T", d)
 	}
 }
 
@@ -468,7 +468,7 @@
 			// don't report an error if the type is an invalid C (defined) type
 			// (issue #22090)
 			if t.Underlying() != Typ[Invalid] {
-				check.errorf(typ.Pos(), _InvalidConstType, "invalid constant type %s", t)
+				check.errorf(typ, _InvalidConstType, "invalid constant type %s", t)
 			}
 			obj.typ = Typ[Invalid]
 			return
@@ -694,9 +694,9 @@
 		if alt := mset.insert(m); alt != nil {
 			switch alt.(type) {
 			case *Var:
-				check.errorf(m.pos, _DuplicateFieldAndMethod, "field and method with the same name %s", m.name)
+				check.errorf(m, _DuplicateFieldAndMethod, "field and method with the same name %s", m.name)
 			case *Func:
-				check.errorf(m.pos, _DuplicateMethod, "method %s already declared for %s", m.name, obj)
+				check.errorf(m, _DuplicateMethod, "method %s already declared for %s", m.name, obj)
 			default:
 				unreachable()
 			}
@@ -721,7 +721,7 @@
 	fdecl := decl.fdecl
 	check.funcType(sig, fdecl.Recv, fdecl.Type)
 	if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
-		check.errorf(fdecl.Pos(), _InvalidInitSig, "func init must have no arguments and no return values")
+		check.errorf(fdecl, _InvalidInitSig, "func init must have no arguments and no return values")
 		// ok to continue
 	}
 
@@ -832,7 +832,7 @@
 			check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign.IsValid())
 			check.pop().setColor(black)
 		default:
-			check.invalidAST(d.node().Pos(), "unknown ast.Decl node %T", d.node())
+			check.invalidAST(d.node(), "unknown ast.Decl node %T", d.node())
 		}
 	})
 }
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
index b721cb1..c9c475e 100644
--- a/src/go/types/errors.go
+++ b/src/go/types/errors.go
@@ -110,43 +110,106 @@
 	f(err)
 }
 
-func (check *Checker) error(pos token.Pos, code errorCode, msg string) {
-	check.err(Error{Fset: check.fset, Pos: pos, Msg: msg, go116code: code})
-}
-
-// newErrorf creates a new Error, but does not handle it.
-func (check *Checker) newErrorf(pos token.Pos, code errorCode, format string, args ...interface{}) error {
+func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
+	ext := spanOf(at)
 	return Error{
-		Fset:      check.fset,
-		Pos:       pos,
-		Msg:       check.sprintf(format, args...),
-		Soft:      false,
-		go116code: code,
+		Fset:       check.fset,
+		Pos:        ext.pos,
+		Msg:        msg,
+		Soft:       soft,
+		go116code:  code,
+		go116start: ext.start,
+		go116end:   ext.end,
 	}
 }
 
-func (check *Checker) errorf(pos token.Pos, code errorCode, format string, args ...interface{}) {
-	check.error(pos, code, check.sprintf(format, args...))
+// newErrorf creates a new Error, but does not handle it.
+func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error {
+	msg := check.sprintf(format, args...)
+	return check.newError(at, code, soft, msg)
 }
 
-func (check *Checker) softErrorf(pos token.Pos, code errorCode, format string, args ...interface{}) {
-	check.err(Error{
-		Fset:      check.fset,
-		Pos:       pos,
-		Msg:       check.sprintf(format, args...),
-		Soft:      true,
-		go116code: code,
-	})
+func (check *Checker) error(at positioner, code errorCode, msg string) {
+	check.err(check.newError(at, code, false, msg))
 }
 
-func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
-	check.errorf(pos, 0, "invalid AST: "+format, args...)
+func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) {
+	check.error(at, code, check.sprintf(format, args...))
 }
 
-func (check *Checker) invalidArg(pos token.Pos, code errorCode, format string, args ...interface{}) {
-	check.errorf(pos, code, "invalid argument: "+format, args...)
+func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) {
+	check.err(check.newErrorf(at, code, true, format, args...))
 }
 
-func (check *Checker) invalidOp(pos token.Pos, code errorCode, format string, args ...interface{}) {
-	check.errorf(pos, code, "invalid operation: "+format, args...)
+func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) {
+	check.errorf(at, 0, "invalid AST: "+format, args...)
+}
+
+func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) {
+	check.errorf(at, code, "invalid argument: "+format, args...)
+}
+
+func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) {
+	check.errorf(at, code, "invalid operation: "+format, args...)
+}
+
+// The positioner interface is used to extract the position of type-checker
+// errors.
+type positioner interface {
+	Pos() token.Pos
+}
+
+// posSpan holds a position range along with a highlighted position within that
+// range. This is used for positioning errors, with pos by convention being the
+// first position in the source where the error is known to exist, and start
+// and end defining the full span of syntax being considered when the error was
+// detected. Invariant: start <= pos < end || start == pos == end.
+type posSpan struct {
+	start, pos, end token.Pos
+}
+
+func (e posSpan) Pos() token.Pos {
+	return e.pos
+}
+
+// inNode creates a posSpan for the given node.
+// Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
+// first byte after node within the source).
+func inNode(node ast.Node, pos token.Pos) posSpan {
+	start, end := node.Pos(), node.End()
+	if debug {
+		assert(start <= pos && pos < end)
+	}
+	return posSpan{start, pos, end}
+}
+
+// atPos wraps a token.Pos to implement the positioner interface.
+type atPos token.Pos
+
+func (s atPos) Pos() token.Pos {
+	return token.Pos(s)
+}
+
+// spanOf extracts an error span from the given positioner. By default this is
+// the trivial span starting and ending at pos, but this span is expanded when
+// the argument naturally corresponds to a span of source code.
+func spanOf(at positioner) posSpan {
+	switch x := at.(type) {
+	case nil:
+		panic("internal error: nil")
+	case posSpan:
+		return x
+	case ast.Node:
+		pos := x.Pos()
+		return posSpan{pos, pos, x.End()}
+	case *operand:
+		if x.expr != nil {
+			pos := x.Pos()
+			return posSpan{pos, pos, x.expr.End()}
+		}
+		return posSpan{token.NoPos, token.NoPos, token.NoPos}
+	default:
+		pos := at.Pos()
+		return posSpan{pos, pos, pos}
+	}
 }
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 5f3415a..1f8b946 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -68,11 +68,11 @@
 func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
 	if pred := m[op]; pred != nil {
 		if !pred(x.typ) {
-			check.invalidOp(x.pos(), _UndefinedOp, "operator %s not defined for %s", op, x)
+			check.invalidOp(x, _UndefinedOp, "operator %s not defined for %s", op, x)
 			return false
 		}
 	} else {
-		check.invalidAST(x.pos(), "unknown operator %s", op)
+		check.invalidAST(x, "unknown operator %s", op)
 		return false
 	}
 	return true
@@ -85,7 +85,7 @@
 		// spec: "As an exception to the addressability
 		// requirement x may also be a composite literal."
 		if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
-			check.invalidOp(x.pos(), _UnaddressableOperand, "cannot take address of %s", x)
+			check.invalidOp(x, _UnaddressableOperand, "cannot take address of %s", x)
 			x.mode = invalid
 			return
 		}
@@ -96,12 +96,12 @@
 	case token.ARROW:
 		typ, ok := x.typ.Underlying().(*Chan)
 		if !ok {
-			check.invalidOp(x.pos(), _InvalidReceive, "cannot receive from non-channel %s", x)
+			check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
 			x.mode = invalid
 			return
 		}
 		if typ.dir == SendOnly {
-			check.invalidOp(x.pos(), _InvalidReceive, "cannot receive from send-only channel %s", x)
+			check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x)
 			x.mode = invalid
 			return
 		}
@@ -362,7 +362,7 @@
 			msg = "cannot convert %s to %s"
 			code = _InvalidConstVal
 		}
-		return check.newErrorf(x.pos(), code, msg, x, typ)
+		return check.newErrorf(x, code, false, msg, x, typ)
 	}
 	return nil
 }
@@ -470,7 +470,7 @@
 		// We already know from the shift check that it is representable
 		// as an integer if it is a constant.
 		if !isInteger(typ) {
-			check.invalidOp(x.Pos(), _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
+			check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
 			return
 		}
 		// Even if we have an integer, if the value is a constant we
@@ -521,7 +521,7 @@
 				check.updateExprType(x.expr, target, false)
 			}
 		} else if xkind != tkind {
-			return check.newErrorf(x.pos(), _InvalidUntypedConversion, "cannot convert %s to %s", x, target)
+			return check.newErrorf(x, _InvalidUntypedConversion, false, "cannot convert %s to %s", x, target)
 		}
 		return nil
 	}
@@ -535,7 +535,7 @@
 	} else {
 		newTarget := check.implicitType(x, target)
 		if newTarget == nil {
-			return check.newErrorf(x.pos(), _InvalidUntypedConversion, "cannot convert %s to %s", x, target)
+			return check.newErrorf(x, _InvalidUntypedConversion, false, "cannot convert %s to %s", x, target)
 		}
 		target = newTarget
 	}
@@ -641,7 +641,7 @@
 	}
 
 	if err != "" {
-		check.errorf(x.pos(), code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+		check.errorf(x, code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
 		x.mode = invalid
 		return
 	}
@@ -678,7 +678,7 @@
 		// as an integer. Nothing to do.
 	} else {
 		// shift has no chance
-		check.invalidOp(x.pos(), _InvalidShiftOperand, "shifted operand %s must be integer", x)
+		check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x)
 		x.mode = invalid
 		return
 	}
@@ -695,7 +695,7 @@
 			return
 		}
 	default:
-		check.invalidOp(y.pos(), _InvalidShiftCount, "shift count %s must be integer", y)
+		check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y)
 		x.mode = invalid
 		return
 	}
@@ -708,7 +708,7 @@
 		yval = constant.ToInt(y.val)
 		assert(yval.Kind() == constant.Int)
 		if constant.Sign(yval) < 0 {
-			check.invalidOp(y.pos(), _InvalidShiftCount, "negative shift count %s", y)
+			check.invalidOp(y, _InvalidShiftCount, "negative shift count %s", y)
 			x.mode = invalid
 			return
 		}
@@ -720,7 +720,7 @@
 			const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
 			s, ok := constant.Uint64Val(yval)
 			if !ok || s > shiftBound {
-				check.invalidOp(y.pos(), _InvalidShiftCount, "invalid shift count %s", y)
+				check.invalidOp(y, _InvalidShiftCount, "invalid shift count %s", y)
 				x.mode = invalid
 				return
 			}
@@ -777,7 +777,7 @@
 
 	// non-constant shift - lhs must be an integer
 	if !isInteger(x.typ) {
-		check.invalidOp(x.pos(), _InvalidShiftOperand, "shifted operand %s must be integer", x)
+		check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x)
 		x.mode = invalid
 		return
 	}
@@ -841,7 +841,11 @@
 		// only report an error if we have valid types
 		// (otherwise we had an error reported elsewhere already)
 		if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
-			check.invalidOp(x.pos(), _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ)
+			var posn positioner = x
+			if e != nil {
+				posn = e
+			}
+			check.invalidOp(posn, _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ)
 		}
 		x.mode = invalid
 		return
@@ -855,7 +859,7 @@
 	if op == token.QUO || op == token.REM {
 		// check for zero divisor
 		if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
-			check.invalidOp(y.pos(), _DivByZero, "division by zero")
+			check.invalidOp(&y, _DivByZero, "division by zero")
 			x.mode = invalid
 			return
 		}
@@ -865,7 +869,7 @@
 			re, im := constant.Real(y.val), constant.Imag(y.val)
 			re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im)
 			if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 {
-				check.invalidOp(y.pos(), _DivByZero, "division by zero")
+				check.invalidOp(&y, _DivByZero, "division by zero")
 				x.mode = invalid
 				return
 			}
@@ -918,7 +922,7 @@
 
 	// the index must be of integer type
 	if !isInteger(x.typ) {
-		check.invalidArg(x.pos(), _InvalidIndex, "index %s must be integer", &x)
+		check.invalidArg(&x, _InvalidIndex, "index %s must be integer", &x)
 		return
 	}
 
@@ -928,13 +932,13 @@
 
 	// a constant index i must be in bounds
 	if constant.Sign(x.val) < 0 {
-		check.invalidArg(x.pos(), _InvalidIndex, "index %s must not be negative", &x)
+		check.invalidArg(&x, _InvalidIndex, "index %s must not be negative", &x)
 		return
 	}
 
 	v, valid := constant.Int64Val(constant.ToInt(x.val))
 	if !valid || max >= 0 && v >= max {
-		check.errorf(x.pos(), _InvalidIndex, "index %s is out of bounds", &x)
+		check.errorf(&x, _InvalidIndex, "index %s is out of bounds", &x)
 		return
 	}
 
@@ -960,12 +964,12 @@
 					index = i
 					validIndex = true
 				} else {
-					check.errorf(e.Pos(), _InvalidLitIndex, "index %s must be integer constant", kv.Key)
+					check.errorf(e, _InvalidLitIndex, "index %s must be integer constant", kv.Key)
 				}
 			}
 			eval = kv.Value
 		} else if length >= 0 && index >= length {
-			check.errorf(e.Pos(), _OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
+			check.errorf(e, _OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
 		} else {
 			validIndex = true
 		}
@@ -973,7 +977,7 @@
 		// if we have a valid index, check for duplicate entries
 		if validIndex {
 			if visited[index] {
-				check.errorf(e.Pos(), _DuplicateLitKey, "duplicate index %d in array or slice literal", index)
+				check.errorf(e, _DuplicateLitKey, "duplicate index %d in array or slice literal", index)
 			}
 			visited[index] = true
 		}
@@ -1063,13 +1067,13 @@
 	case *ast.Ellipsis:
 		// ellipses are handled explicitly where they are legal
 		// (array composite literals and parameter lists)
-		check.error(e.Pos(), _BadDotDotDotSyntax, "invalid use of '...'")
+		check.error(e, _BadDotDotDotSyntax, "invalid use of '...'")
 		goto Error
 
 	case *ast.BasicLit:
 		x.setConst(e.Kind, e.Value)
 		if x.mode == invalid {
-			check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
+			check.invalidAST(e, "invalid literal %v", e.Value)
 			goto Error
 		}
 
@@ -1090,7 +1094,7 @@
 			x.mode = value
 			x.typ = sig
 		} else {
-			check.invalidAST(e.Pos(), "invalid function literal %s", e)
+			check.invalidAST(e, "invalid function literal %s", e)
 			goto Error
 		}
 
@@ -1122,7 +1126,7 @@
 
 		default:
 			// TODO(gri) provide better error messages depending on context
-			check.error(e.Pos(), _UntypedLit, "missing type in composite literal")
+			check.error(e, _UntypedLit, "missing type in composite literal")
 			goto Error
 		}
 
@@ -1138,7 +1142,7 @@
 				for _, e := range e.Elts {
 					kv, _ := e.(*ast.KeyValueExpr)
 					if kv == nil {
-						check.error(e.Pos(), _MixedStructLit, "mixture of field:value and value elements in struct literal")
+						check.error(e, _MixedStructLit, "mixture of field:value and value elements in struct literal")
 						continue
 					}
 					key, _ := kv.Key.(*ast.Ident)
@@ -1146,12 +1150,12 @@
 					// so we don't drop information on the floor
 					check.expr(x, kv.Value)
 					if key == nil {
-						check.errorf(kv.Pos(), _InvalidLitField, "invalid field name %s in struct literal", kv.Key)
+						check.errorf(kv, _InvalidLitField, "invalid field name %s in struct literal", kv.Key)
 						continue
 					}
 					i := fieldIndex(utyp.fields, check.pkg, key.Name)
 					if i < 0 {
-						check.errorf(kv.Pos(), _MissingLitField, "unknown field %s in struct literal", key.Name)
+						check.errorf(kv, _MissingLitField, "unknown field %s in struct literal", key.Name)
 						continue
 					}
 					fld := fields[i]
@@ -1160,7 +1164,7 @@
 					check.assignment(x, etyp, "struct literal")
 					// 0 <= i < len(fields)
 					if visited[i] {
-						check.errorf(kv.Pos(), _DuplicateLitField, "duplicate field name %s in struct literal", key.Name)
+						check.errorf(kv, _DuplicateLitField, "duplicate field name %s in struct literal", key.Name)
 						continue
 					}
 					visited[i] = true
@@ -1169,25 +1173,27 @@
 				// no element must have a key
 				for i, e := range e.Elts {
 					if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
-						check.error(kv.Pos(), _MixedStructLit, "mixture of field:value and value elements in struct literal")
+						check.error(kv, _MixedStructLit, "mixture of field:value and value elements in struct literal")
 						continue
 					}
 					check.expr(x, e)
 					if i >= len(fields) {
-						check.error(x.pos(), _InvalidStructLit, "too many values in struct literal")
+						check.error(x, _InvalidStructLit, "too many values in struct literal")
 						break // cannot continue
 					}
 					// i < len(fields)
 					fld := fields[i]
 					if !fld.Exported() && fld.pkg != check.pkg {
-						check.errorf(x.pos(), _UnexportedLitField, "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+						check.errorf(x,
+							_UnexportedLitField,
+							"implicit assignment to unexported field %s in %s literal", fld.name, typ)
 						continue
 					}
 					etyp := fld.typ
 					check.assignment(x, etyp, "struct literal")
 				}
 				if len(e.Elts) < len(fields) {
-					check.error(e.Rbrace, _InvalidStructLit, "too few values in struct literal")
+					check.error(inNode(e, e.Rbrace), _InvalidStructLit, "too few values in struct literal")
 					// ok to continue
 				}
 			}
@@ -1197,7 +1203,7 @@
 			// This is a stop-gap solution. Should use Checker.objPath to report entire
 			// path starting with earliest declaration in the source. TODO(gri) fix this.
 			if utyp.elem == nil {
-				check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
+				check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
 				goto Error
 			}
 			n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
@@ -1224,7 +1230,7 @@
 			// Prevent crash if the slice referred to is not yet set up.
 			// See analogous comment for *Array.
 			if utyp.elem == nil {
-				check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
+				check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
 				goto Error
 			}
 			check.indexedElts(e.Elts, utyp.elem, -1)
@@ -1233,14 +1239,14 @@
 			// Prevent crash if the map referred to is not yet set up.
 			// See analogous comment for *Array.
 			if utyp.key == nil || utyp.elem == nil {
-				check.error(e.Pos(), _InvalidTypeCycle, "illegal cycle in type declaration")
+				check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
 				goto Error
 			}
 			visited := make(map[interface{}][]Type, len(e.Elts))
 			for _, e := range e.Elts {
 				kv, _ := e.(*ast.KeyValueExpr)
 				if kv == nil {
-					check.error(e.Pos(), _MissingLitKey, "missing key in map literal")
+					check.error(e, _MissingLitKey, "missing key in map literal")
 					continue
 				}
 				check.exprWithHint(x, kv.Key, utyp.key)
@@ -1265,7 +1271,7 @@
 						visited[xkey] = nil
 					}
 					if duplicate {
-						check.errorf(x.pos(), _DuplicateLitKey, "duplicate key %s in map literal", x.val)
+						check.errorf(x, _DuplicateLitKey, "duplicate key %s in map literal", x.val)
 						continue
 					}
 				}
@@ -1287,7 +1293,7 @@
 			}
 			// if utyp is invalid, an error was reported before
 			if utyp != Typ[Invalid] {
-				check.errorf(e.Pos(), _InvalidLit, "invalid composite literal type %s", typ)
+				check.errorf(e, _InvalidLit, "invalid composite literal type %s", typ)
 				goto Error
 			}
 		}
@@ -1361,12 +1367,12 @@
 		}
 
 		if !valid {
-			check.invalidOp(x.pos(), _NonIndexableOperand, "cannot index %s", x)
+			check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
 			goto Error
 		}
 
 		if e.Index == nil {
-			check.invalidAST(e.Pos(), "missing index for %s", x)
+			check.invalidAST(e, "missing index for %s", x)
 			goto Error
 		}
 
@@ -1386,7 +1392,7 @@
 		case *Basic:
 			if isString(typ) {
 				if e.Slice3 {
-					check.invalidOp(x.pos(), _InvalidSliceExpr, "3-index slice of string")
+					check.invalidOp(x, _InvalidSliceExpr, "3-index slice of string")
 					goto Error
 				}
 				valid = true
@@ -1404,7 +1410,7 @@
 			valid = true
 			length = typ.len
 			if x.mode != variable {
-				check.invalidOp(x.pos(), _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
+				check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
 				goto Error
 			}
 			x.typ = &Slice{elem: typ.elem}
@@ -1422,7 +1428,7 @@
 		}
 
 		if !valid {
-			check.invalidOp(x.pos(), _NonSliceableOperand, "cannot slice %s", x)
+			check.invalidOp(x, _NonSliceableOperand, "cannot slice %s", x)
 			goto Error
 		}
 
@@ -1430,7 +1436,7 @@
 
 		// spec: "Only the first index may be omitted; it defaults to 0."
 		if e.Slice3 && (e.High == nil || e.Max == nil) {
-			check.invalidAST(e.Rbrack, "2nd and 3rd index required in 3-index slice")
+			check.invalidAST(inNode(e, e.Rbrack), "2nd and 3rd index required in 3-index slice")
 			goto Error
 		}
 
@@ -1467,7 +1473,7 @@
 			if x > 0 {
 				for _, y := range ind[i+1:] {
 					if y >= 0 && x > y {
-						check.errorf(e.Rbrack, _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y)
+						check.errorf(inNode(e, e.Rbrack), _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y)
 						break L // only report one error, ok to continue
 					}
 				}
@@ -1481,21 +1487,21 @@
 		}
 		xtyp, _ := x.typ.Underlying().(*Interface)
 		if xtyp == nil {
-			check.invalidOp(x.pos(), _InvalidAssert, "%s is not an interface", x)
+			check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
 			goto Error
 		}
 		// x.(type) expressions are handled explicitly in type switches
 		if e.Type == nil {
 			// Don't use invalidAST because this can occur in the AST produced by
 			// go/parser.
-			check.error(e.Pos(), _BadTypeKeyword, "use of .(type) outside type switch")
+			check.error(e, _BadTypeKeyword, "use of .(type) outside type switch")
 			goto Error
 		}
 		T := check.typ(e.Type)
 		if T == Typ[Invalid] {
 			goto Error
 		}
-		check.typeAssertion(x.pos(), x, xtyp, T)
+		check.typeAssertion(x, x, xtyp, T)
 		x.mode = commaok
 		x.typ = T
 
@@ -1514,7 +1520,7 @@
 				x.mode = variable
 				x.typ = typ.base
 			} else {
-				check.invalidOp(x.pos(), _InvalidIndirection, "cannot indirect %s", x)
+				check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x)
 				goto Error
 			}
 		}
@@ -1541,7 +1547,7 @@
 
 	case *ast.KeyValueExpr:
 		// key:value expressions are handled in composite literals
-		check.invalidAST(e.Pos(), "no key:value expected")
+		check.invalidAST(e, "no key:value expected")
 		goto Error
 
 	case *ast.ArrayType, *ast.StructType, *ast.FuncType,
@@ -1593,7 +1599,7 @@
 }
 
 // typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
-func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
+func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, T Type) {
 	method, wrongType := check.assertableTo(xtyp, T)
 	if method == nil {
 		return
@@ -1608,7 +1614,7 @@
 	} else {
 		msg = "missing method " + method.name
 	}
-	check.errorf(pos, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
+	check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
 }
 
 func (check *Checker) singleValue(x *operand) {
@@ -1616,7 +1622,7 @@
 		// tuple types are never named - no need for underlying type below
 		if t, ok := x.typ.(*Tuple); ok {
 			assert(t.Len() != 1)
-			check.errorf(x.pos(), _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
+			check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
 			x.mode = invalid
 		}
 	}
@@ -1649,7 +1655,7 @@
 		msg = "%s is not an expression"
 		code = _NotAnExpr
 	}
-	check.errorf(x.pos(), code, msg, x)
+	check.errorf(x, code, msg, x)
 	x.mode = invalid
 }
 
@@ -1676,7 +1682,7 @@
 		msg = "%s is not an expression"
 		code = _NotAnExpr
 	}
-	check.errorf(x.pos(), code, msg, x)
+	check.errorf(x, code, msg, x)
 	x.mode = invalid
 }
 
@@ -1687,7 +1693,7 @@
 	check.rawExpr(x, e, nil)
 	check.singleValue(x)
 	if x.mode == novalue {
-		check.errorf(x.pos(), _NotAnExpr, "%s used as value or type", x)
+		check.errorf(x, _NotAnExpr, "%s used as value or type", x)
 		x.mode = invalid
 	}
 }
diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go
index f770d8b..77a739c 100644
--- a/src/go/types/initorder.go
+++ b/src/go/types/initorder.go
@@ -151,14 +151,14 @@
 // reportCycle reports an error for the given cycle.
 func (check *Checker) reportCycle(cycle []Object) {
 	obj := cycle[0]
-	check.errorf(obj.Pos(), _InvalidInitCycle, "initialization cycle for %s", obj.Name())
+	check.errorf(obj, _InvalidInitCycle, "initialization cycle for %s", obj.Name())
 	// subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n
 	for i := len(cycle) - 1; i >= 0; i-- {
-		check.errorf(obj.Pos(), _InvalidInitCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
+		check.errorf(obj, _InvalidInitCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
 		obj = cycle[i]
 	}
 	// print cycle[0] again to close the cycle
-	check.errorf(obj.Pos(), _InvalidInitCycle, "\t%s", obj.Name())
+	check.errorf(obj, _InvalidInitCycle, "\t%s", obj.Name())
 }
 
 // ----------------------------------------------------------------------------
diff --git a/src/go/types/labels.go b/src/go/types/labels.go
index 5a577c4..8cf6e63 100644
--- a/src/go/types/labels.go
+++ b/src/go/types/labels.go
@@ -32,13 +32,13 @@
 			msg = "label %s not declared"
 			code = _UndeclaredLabel
 		}
-		check.errorf(jmp.Label.Pos(), code, msg, name)
+		check.errorf(jmp.Label, code, msg, name)
 	}
 
 	// spec: "It is illegal to define a label that is never used."
 	for _, obj := range all.elems {
 		if lbl := obj.(*Label); !lbl.used {
-			check.softErrorf(lbl.pos, _UnusedLabel, "label %s declared but not used", lbl.name)
+			check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name)
 		}
 	}
 }
@@ -136,7 +136,7 @@
 			if name := s.Label.Name; name != "_" {
 				lbl := NewLabel(s.Label.Pos(), check.pkg, name)
 				if alt := all.Insert(lbl); alt != nil {
-					check.softErrorf(lbl.pos, _DuplicateLabel, "label %s already declared", name)
+					check.softErrorf(lbl, _DuplicateLabel, "label %s already declared", name)
 					check.reportAltDecl(alt)
 					// ok to continue
 				} else {
@@ -152,7 +152,7 @@
 						check.recordUse(jmp.Label, lbl)
 						if jumpsOverVarDecl(jmp) {
 							check.softErrorf(
-								jmp.Label.Pos(),
+								jmp.Label,
 								_JumpOverDecl,
 								"goto %s jumps over variable declaration at line %d",
 								name,
@@ -191,7 +191,7 @@
 					}
 				}
 				if !valid {
-					check.errorf(s.Label.Pos(), _MisplacedLabel, "invalid break label %s", name)
+					check.errorf(s.Label, _MisplacedLabel, "invalid break label %s", name)
 					return
 				}
 
@@ -206,7 +206,7 @@
 					}
 				}
 				if !valid {
-					check.errorf(s.Label.Pos(), _MisplacedLabel, "invalid continue label %s", name)
+					check.errorf(s.Label, _MisplacedLabel, "invalid continue label %s", name)
 					return
 				}
 
@@ -218,7 +218,7 @@
 				}
 
 			default:
-				check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
+				check.invalidAST(s, "branch statement: %s %s", s.Tok, name)
 				return
 			}
 
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index 73b3be2..2d30dbd 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -59,10 +59,10 @@
 	id   builtinId
 }
 
-// pos returns the position of the expression corresponding to x.
+// Pos returns the position of the expression corresponding to x.
 // If x is invalid the position is token.NoPos.
 //
-func (x *operand) pos() token.Pos {
+func (x *operand) Pos() token.Pos {
 	// x.expr may not be set if x is invalid
 	if x.expr == nil {
 		return token.NoPos
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 05d24ef..4092d55 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -60,22 +60,22 @@
 	case init == nil && r == 0:
 		// var decl w/o init expr
 		if s.Type == nil {
-			check.errorf(s.Pos(), code, "missing type or init expr")
+			check.errorf(s, code, "missing type or init expr")
 		}
 	case l < r:
 		if l < len(s.Values) {
 			// init exprs from s
 			n := s.Values[l]
-			check.errorf(n.Pos(), code, "extra init expr %s", n)
+			check.errorf(n, code, "extra init expr %s", n)
 			// TODO(gri) avoid declared but not used error here
 		} else {
 			// init exprs "inherited"
-			check.errorf(s.Pos(), code, "extra init expr at %s", check.fset.Position(init.Pos()))
+			check.errorf(s, code, "extra init expr at %s", check.fset.Position(init.Pos()))
 			// TODO(gri) avoid declared but not used error here
 		}
 	case l > r && (init != nil || r != 1):
 		n := s.Names[r]
-		check.errorf(n.Pos(), code, "missing init expr for %s", n)
+		check.errorf(n, code, "missing init expr for %s", n)
 	}
 }
 
@@ -104,14 +104,14 @@
 	// spec: "A package-scope or file-scope identifier with name init
 	// may only be declared to be a function with this (func()) signature."
 	if ident.Name == "init" {
-		check.errorf(ident.Pos(), _InvalidInitDecl, "cannot declare init - must be func")
+		check.errorf(ident, _InvalidInitDecl, "cannot declare init - must be func")
 		return
 	}
 
 	// spec: "The main package must have package name main and declare
 	// a function main that takes no arguments and returns no value."
 	if ident.Name == "main" && check.pkg.name == "main" {
-		check.errorf(ident.Pos(), _InvalidMainDecl, "cannot declare main - must be func")
+		check.errorf(ident, _InvalidMainDecl, "cannot declare main - must be func")
 		return
 	}
 
@@ -169,7 +169,7 @@
 			imp = nil // create fake package below
 		}
 		if err != nil {
-			check.errorf(pos, _BrokenImport, "could not import %s (%s)", path, err)
+			check.errorf(atPos(pos), _BrokenImport, "could not import %s (%s)", path, err)
 			if imp == nil {
 				// create a new fake package
 				// come up with a sensible package name (heuristic)
@@ -242,7 +242,7 @@
 				// import package
 				path, err := validatedImportPath(d.spec.Path.Value)
 				if err != nil {
-					check.errorf(d.spec.Path.Pos(), _BadImportPath, "invalid import path (%s)", err)
+					check.errorf(d.spec.Path, _BadImportPath, "invalid import path (%s)", err)
 					return
 				}
 
@@ -265,11 +265,11 @@
 					name = d.spec.Name.Name
 					if path == "C" {
 						// match cmd/compile (not prescribed by spec)
-						check.errorf(d.spec.Name.Pos(), _ImportCRenamed, `cannot rename import "C"`)
+						check.errorf(d.spec.Name, _ImportCRenamed, `cannot rename import "C"`)
 						return
 					}
 					if name == "init" {
-						check.errorf(d.spec.Name.Pos(), _InvalidInitDecl, "cannot declare init - must be func")
+						check.errorf(d.spec.Name, _InvalidInitDecl, "cannot declare init - must be func")
 						return
 					}
 				}
@@ -300,14 +300,14 @@
 							// the object may be imported into more than one file scope
 							// concurrently. See issue #32154.)
 							if alt := fileScope.Insert(obj); alt != nil {
-								check.errorf(d.spec.Name.Pos(), _DuplicateDecl, "%s redeclared in this block", obj.Name())
+								check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
 								check.reportAltDecl(alt)
 							}
 						}
 					}
 					// add position to set of dot-import positions for this file
 					// (this is only needed for "imported but not used" errors)
-					check.addUnusedDotImport(fileScope, imp, d.spec.Pos())
+					check.addUnusedDotImport(fileScope, imp, d.spec)
 				} else {
 					// declare imported package object in file scope
 					// (no need to provide s.Name since we called check.recordDef earlier)
@@ -373,7 +373,7 @@
 						check.recordDef(d.decl.Name, obj)
 						// init functions must have a body
 						if d.decl.Body == nil {
-							check.softErrorf(obj.pos, _MissingInitBody, "missing function body")
+							check.softErrorf(obj, _MissingInitBody, "missing function body")
 						}
 					} else {
 						check.declare(pkg.scope, d.decl.Name, obj, token.NoPos)
@@ -403,10 +403,10 @@
 		for _, obj := range scope.elems {
 			if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
 				if pkg, ok := obj.(*PkgName); ok {
-					check.errorf(alt.Pos(), _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported())
+					check.errorf(alt, _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported())
 					check.reportAltDecl(pkg)
 				} else {
-					check.errorf(alt.Pos(), _DuplicateDecl, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+					check.errorf(alt, _DuplicateDecl, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
 					// TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
 					check.reportAltDecl(obj)
 				}
@@ -575,9 +575,9 @@
 					path := obj.imported.path
 					base := pkgName(path)
 					if obj.name == base {
-						check.softErrorf(obj.pos, _UnusedImport, "%q imported but not used", path)
+						check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
 					} else {
-						check.softErrorf(obj.pos, _UnusedImport, "%q imported but not used as %s", path, obj.name)
+						check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
 					}
 				}
 			}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index cc98b07..b1ccbf0 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -46,7 +46,7 @@
 	}
 
 	if sig.results.Len() > 0 && !check.isTerminating(body, "") {
-		check.error(body.Rbrace, _MissingReturn, "missing return")
+		check.error(atPos(body.Rbrace), _MissingReturn, "missing return")
 	}
 
 	// spec: "Implementation restriction: A compiler may make it illegal to
@@ -65,7 +65,7 @@
 		return unused[i].pos < unused[j].pos
 	})
 	for _, v := range unused {
-		check.softErrorf(v.pos, _UnusedVar, "%s declared but not used", v.name)
+		check.softErrorf(v, _UnusedVar, "%s declared but not used", v.name)
 	}
 
 	for _, scope := range scope.children {
@@ -135,11 +135,11 @@
 				d = s
 			}
 		default:
-			check.invalidAST(s.Pos(), "case/communication clause expected")
+			check.invalidAST(s, "case/communication clause expected")
 		}
 		if d != nil {
 			if first != nil {
-				check.errorf(d.Pos(), _DuplicateDefault, "multiple defaults (first at %s)", check.fset.Position(first.Pos()))
+				check.errorf(d, _DuplicateDefault, "multiple defaults (first at %s)", check.fset.Position(first.Pos()))
 			} else {
 				first = d
 			}
@@ -184,7 +184,7 @@
 	default:
 		unreachable()
 	}
-	check.errorf(x.pos(), code, "%s %s %s", keyword, msg, &x)
+	check.errorf(&x, code, "%s %s %s", keyword, msg, &x)
 }
 
 // goVal returns the Go value for val, or nil.
@@ -256,17 +256,17 @@
 			// (quadratic algorithm, but these lists tend to be very short)
 			for _, vt := range seen[val] {
 				if check.identical(v.typ, vt.typ) {
-					check.errorf(v.pos(), _DuplicateCase, "duplicate case %s in expression switch", &v)
-					check.error(vt.pos, _DuplicateCase, "\tprevious case") // secondary error, \t indented
+					check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
+					check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented
 					continue L
 				}
 			}
-			seen[val] = append(seen[val], valueType{v.pos(), v.typ})
+			seen[val] = append(seen[val], valueType{v.Pos(), v.typ})
 		}
 	}
 }
 
-func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
+func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
 L:
 	for _, e := range types {
 		T = check.typOrNil(e)
@@ -275,21 +275,21 @@
 		}
 		// look for duplicate types
 		// (quadratic algorithm, but type switches tend to be reasonably small)
-		for t, pos := range seen {
+		for t, other := range seen {
 			if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
 				// talk about "case" rather than "type" because of nil case
 				Ts := "nil"
 				if T != nil {
 					Ts = T.String()
 				}
-				check.errorf(e.Pos(), _DuplicateCase, "duplicate case %s in type switch", Ts)
-				check.error(pos, _DuplicateCase, "\tprevious case") // secondary error, \t indented
+				check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
+				check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
 				continue L
 			}
 		}
-		seen[T] = e.Pos()
+		seen[T] = e
 		if T != nil {
-			check.typeAssertion(e.Pos(), x, xtyp, T)
+			check.typeAssertion(e, x, xtyp, T)
 		}
 	}
 	return
@@ -345,7 +345,7 @@
 			msg = "is not an expression"
 			code = _NotAnExpr
 		}
-		check.errorf(x.pos(), code, "%s %s", &x, msg)
+		check.errorf(&x, code, "%s %s", &x, msg)
 
 	case *ast.SendStmt:
 		var ch, x operand
@@ -357,12 +357,12 @@
 
 		tch, ok := ch.typ.Underlying().(*Chan)
 		if !ok {
-			check.invalidOp(s.Arrow, _InvalidSend, "cannot send to non-chan type %s", ch.typ)
+			check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
 			return
 		}
 
 		if tch.dir == RecvOnly {
-			check.invalidOp(s.Arrow, _InvalidSend, "cannot send to receive-only type %s", tch)
+			check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only type %s", tch)
 			return
 		}
 
@@ -376,7 +376,7 @@
 		case token.DEC:
 			op = token.SUB
 		default:
-			check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
+			check.invalidAST(inNode(s, s.TokPos), "unknown inc/dec operation %s", s.Tok)
 			return
 		}
 
@@ -386,7 +386,7 @@
 			return
 		}
 		if !isNumeric(x.typ) {
-			check.invalidOp(s.X.Pos(), _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
+			check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
 			return
 		}
 
@@ -401,11 +401,11 @@
 		switch s.Tok {
 		case token.ASSIGN, token.DEFINE:
 			if len(s.Lhs) == 0 {
-				check.invalidAST(s.Pos(), "missing lhs in assignment")
+				check.invalidAST(s, "missing lhs in assignment")
 				return
 			}
 			if s.Tok == token.DEFINE {
-				check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
+				check.shortVarDecl(inNode(s, s.TokPos), s.Lhs, s.Rhs)
 			} else {
 				// regular assignment
 				check.assignVars(s.Lhs, s.Rhs)
@@ -414,12 +414,12 @@
 		default:
 			// assignment operations
 			if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
-				check.errorf(s.TokPos, _MultiValAssignOp, "assignment operation %s requires single-valued expressions", s.Tok)
+				check.errorf(inNode(s, s.TokPos), _MultiValAssignOp, "assignment operation %s requires single-valued expressions", s.Tok)
 				return
 			}
 			op := assignOp(s.Tok)
 			if op == token.ILLEGAL {
-				check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
+				check.invalidAST(atPos(s.TokPos), "unknown assignment operation %s", s.Tok)
 				return
 			}
 			var x operand
@@ -447,8 +447,8 @@
 				// with the same name as a result parameter is in scope at the place of the return."
 				for _, obj := range res.vars {
 					if alt := check.lookup(obj.name); alt != nil && alt != obj {
-						check.errorf(s.Pos(), _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
-						check.errorf(alt.Pos(), _OutOfScopeResult, "\tinner declaration of %s", obj)
+						check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
+						check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
 						// ok to continue
 					}
 				}
@@ -457,7 +457,7 @@
 				check.initVars(res.vars, s.Results, s.Return)
 			}
 		} else if len(s.Results) > 0 {
-			check.error(s.Results[0].Pos(), _WrongResultCount, "no result values expected")
+			check.error(s.Results[0], _WrongResultCount, "no result values expected")
 			check.use(s.Results...)
 		}
 
@@ -469,11 +469,11 @@
 		switch s.Tok {
 		case token.BREAK:
 			if ctxt&breakOk == 0 {
-				check.error(s.Pos(), _MisplacedBreak, "break not in for, switch, or select statement")
+				check.error(s, _MisplacedBreak, "break not in for, switch, or select statement")
 			}
 		case token.CONTINUE:
 			if ctxt&continueOk == 0 {
-				check.error(s.Pos(), _MisplacedContinue, "continue not in for statement")
+				check.error(s, _MisplacedContinue, "continue not in for statement")
 			}
 		case token.FALLTHROUGH:
 			if ctxt&fallthroughOk == 0 {
@@ -482,10 +482,10 @@
 				if ctxt&finalSwitchCase != 0 {
 					msg = "cannot fallthrough final case in switch"
 				}
-				check.error(s.Pos(), code, msg)
+				check.error(s, code, msg)
 			}
 		default:
-			check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
+			check.invalidAST(s, "branch statement: %s", s.Tok)
 		}
 
 	case *ast.BlockStmt:
@@ -502,7 +502,7 @@
 		var x operand
 		check.expr(&x, s.Cond)
 		if x.mode != invalid && !isBoolean(x.typ) {
-			check.error(s.Cond.Pos(), _InvalidCond, "non-boolean condition in if statement")
+			check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement")
 		}
 		check.stmt(inner, s.Body)
 		// The parser produces a correct AST but if it was modified
@@ -513,7 +513,7 @@
 		case *ast.IfStmt, *ast.BlockStmt:
 			check.stmt(inner, s.Else)
 		default:
-			check.invalidAST(s.Else.Pos(), "invalid else branch in if statement")
+			check.invalidAST(s.Else, "invalid else branch in if statement")
 		}
 
 	case *ast.SwitchStmt:
@@ -543,7 +543,7 @@
 		for i, c := range s.Body.List {
 			clause, _ := c.(*ast.CaseClause)
 			if clause == nil {
-				check.invalidAST(c.Pos(), "incorrect expression switch case")
+				check.invalidAST(c, "incorrect expression switch case")
 				continue
 			}
 			check.caseValues(&x, clause.List, seen)
@@ -580,19 +580,19 @@
 			rhs = guard.X
 		case *ast.AssignStmt:
 			if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
-				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+				check.invalidAST(s, "incorrect form of type switch guard")
 				return
 			}
 
 			lhs, _ = guard.Lhs[0].(*ast.Ident)
 			if lhs == nil {
-				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+				check.invalidAST(s, "incorrect form of type switch guard")
 				return
 			}
 
 			if lhs.Name == "_" {
 				// _ := x.(type) is an invalid short variable declaration
-				check.softErrorf(lhs.Pos(), _NoNewVar, "no new variable on left side of :=")
+				check.softErrorf(lhs, _NoNewVar, "no new variable on left side of :=")
 				lhs = nil // avoid declared but not used error below
 			} else {
 				check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
@@ -601,14 +601,14 @@
 			rhs = guard.Rhs[0]
 
 		default:
-			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+			check.invalidAST(s, "incorrect form of type switch guard")
 			return
 		}
 
 		// rhs must be of the form: expr.(type) and expr must be an interface
 		expr, _ := rhs.(*ast.TypeAssertExpr)
 		if expr == nil || expr.Type != nil {
-			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+			check.invalidAST(s, "incorrect form of type switch guard")
 			return
 		}
 		var x operand
@@ -618,18 +618,18 @@
 		}
 		xtyp, _ := x.typ.Underlying().(*Interface)
 		if xtyp == nil {
-			check.errorf(x.pos(), _InvalidTypeSwitch, "%s is not an interface", &x)
+			check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
 			return
 		}
 
 		check.multipleDefaults(s.Body.List)
 
-		var lhsVars []*Var               // list of implicitly declared lhs variables
-		seen := make(map[Type]token.Pos) // map of seen types to positions
+		var lhsVars []*Var              // list of implicitly declared lhs variables
+		seen := make(map[Type]ast.Expr) // map of seen types to positions
 		for _, s := range s.Body.List {
 			clause, _ := s.(*ast.CaseClause)
 			if clause == nil {
-				check.invalidAST(s.Pos(), "incorrect type switch case")
+				check.invalidAST(s, "incorrect type switch case")
 				continue
 			}
 			// Check each type in this type switch case.
@@ -671,7 +671,7 @@
 				v.used = true // avoid usage error when checking entire function
 			}
 			if !used {
-				check.softErrorf(lhs.Pos(), _UnusedVar, "%s declared but not used", lhs.Name)
+				check.softErrorf(lhs, _UnusedVar, "%s declared but not used", lhs.Name)
 			}
 		}
 
@@ -708,7 +708,7 @@
 			}
 
 			if !valid {
-				check.error(clause.Comm.Pos(), _InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
+				check.error(clause.Comm, _InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
 				continue
 			}
 
@@ -730,14 +730,14 @@
 			var x operand
 			check.expr(&x, s.Cond)
 			if x.mode != invalid && !isBoolean(x.typ) {
-				check.error(s.Cond.Pos(), _InvalidCond, "non-boolean condition in for statement")
+				check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement")
 			}
 		}
 		check.simpleStmt(s.Post)
 		// spec: "The init statement may be a short variable
 		// declaration, but the post statement must not."
 		if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
-			check.softErrorf(s.Pos(), _InvalidPostDecl, "cannot declare in post statement")
+			check.softErrorf(s, _InvalidPostDecl, "cannot declare in post statement")
 			// Don't call useLHS here because we want to use the lhs in
 			// this erroneous statement so that we don't get errors about
 			// these lhs variables being declared but not used.
@@ -781,18 +781,18 @@
 				key = typ.elem
 				val = Typ[Invalid]
 				if typ.dir == SendOnly {
-					check.errorf(x.pos(), _InvalidChanRange, "cannot range over send-only channel %s", &x)
+					check.errorf(&x, _InvalidChanRange, "cannot range over send-only channel %s", &x)
 					// ok to continue
 				}
 				if s.Value != nil {
-					check.errorf(s.Value.Pos(), _InvalidIterVar, "iteration over %s permits only one iteration variable", &x)
+					check.errorf(atPos(s.Value.Pos()), _InvalidIterVar, "iteration over %s permits only one iteration variable", &x)
 					// ok to continue
 				}
 			}
 		}
 
 		if key == nil {
-			check.errorf(x.pos(), _InvalidRangeExpr, "cannot range over %s", &x)
+			check.errorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
 			// ok to continue
 		}
 
@@ -825,7 +825,7 @@
 						vars = append(vars, obj)
 					}
 				} else {
-					check.invalidAST(lhs.Pos(), "cannot declare %s", lhs)
+					check.invalidAST(lhs, "cannot declare %s", lhs)
 					obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
 				}
 
@@ -852,7 +852,7 @@
 					check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
 				}
 			} else {
-				check.error(s.TokPos, _NoNewVar, "no new variables on left side of :=")
+				check.error(inNode(s, s.TokPos), _NoNewVar, "no new variables on left side of :=")
 			}
 		} else {
 			// ordinary assignment
@@ -872,6 +872,6 @@
 		check.stmt(inner, s.Body)
 
 	default:
-		check.invalidAST(s.Pos(), "invalid statement")
+		check.invalidAST(s, "invalid statement")
 	}
 }
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index b038704..2b39801 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -28,9 +28,9 @@
 	scope, obj := check.scope.LookupParent(e.Name, check.pos)
 	if obj == nil {
 		if e.Name == "_" {
-			check.errorf(e.Pos(), _InvalidBlank, "cannot use _ as value or type")
+			check.errorf(e, _InvalidBlank, "cannot use _ as value or type")
 		} else {
-			check.errorf(e.Pos(), _UndeclaredName, "undeclared name: %s", e.Name)
+			check.errorf(e, _UndeclaredName, "undeclared name: %s", e.Name)
 		}
 		return
 	}
@@ -61,7 +61,7 @@
 
 	switch obj := obj.(type) {
 	case *PkgName:
-		check.errorf(e.Pos(), _InvalidPkgUse, "use of package %s not in selector", obj.name)
+		check.errorf(e, _InvalidPkgUse, "use of package %s not in selector", obj.name)
 		return
 
 	case *Const:
@@ -71,7 +71,7 @@
 		}
 		if obj == universeIota {
 			if check.iota == nil {
-				check.errorf(e.Pos(), _InvalidIota, "cannot use iota outside constant declaration")
+				check.errorf(e, _InvalidIota, "cannot use iota outside constant declaration")
 				return
 			}
 			x.val = check.iota
@@ -159,11 +159,11 @@
 		var recv *Var
 		switch len(recvList) {
 		case 0:
-			check.error(recvPar.Pos(), _BadRecv, "method is missing receiver")
+			check.error(recvPar, _BadRecv, "method is missing receiver")
 			recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
 		default:
 			// more than one receiver
-			check.error(recvList[len(recvList)-1].Pos(), _BadRecv, "method must have exactly one receiver")
+			check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
 			fallthrough // continue with first receiver
 		case 1:
 			recv = recvList[0]
@@ -194,7 +194,7 @@
 				err = "basic or unnamed type"
 			}
 			if err != "" {
-				check.errorf(recv.pos, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err)
+				check.errorf(recv, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err)
 				// ok to continue
 			}
 		}
@@ -227,9 +227,9 @@
 		case invalid:
 			// ignore - error reported before
 		case novalue:
-			check.errorf(x.pos(), _NotAType, "%s used as type", &x)
+			check.errorf(&x, _NotAType, "%s used as type", &x)
 		default:
-			check.errorf(x.pos(), _NotAType, "%s is not a type", &x)
+			check.errorf(&x, _NotAType, "%s is not a type", &x)
 		}
 
 	case *ast.SelectorExpr:
@@ -244,9 +244,9 @@
 		case invalid:
 			// ignore - error reported before
 		case novalue:
-			check.errorf(x.pos(), _NotAType, "%s used as type", &x)
+			check.errorf(&x, _NotAType, "%s used as type", &x)
 		default:
-			check.errorf(x.pos(), _NotAType, "%s is not a type", &x)
+			check.errorf(&x, _NotAType, "%s is not a type", &x)
 		}
 
 	case *ast.ParenExpr:
@@ -306,7 +306,7 @@
 		// it is safe to continue in any case (was issue 6667).
 		check.atEnd(func() {
 			if !Comparable(typ.key) {
-				check.errorf(e.Key.Pos(), _IncomparableMapKey, "incomparable map key type %s", typ.key)
+				check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s", typ.key)
 			}
 		})
 
@@ -325,7 +325,7 @@
 		case ast.RECV:
 			dir = RecvOnly
 		default:
-			check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
+			check.invalidAST(e, "unknown channel direction %d", e.Dir)
 			// ok to continue
 		}
 
@@ -334,7 +334,7 @@
 		return typ
 
 	default:
-		check.errorf(e.Pos(), _NotAType, "%s is not a type", e)
+		check.errorf(e, _NotAType, "%s is not a type", e)
 	}
 
 	typ := Typ[Invalid]
@@ -353,7 +353,7 @@
 	case invalid:
 		// ignore - error reported before
 	case novalue:
-		check.errorf(x.pos(), _NotAType, "%s used as type", &x)
+		check.errorf(&x, _NotAType, "%s used as type", &x)
 	case typexpr:
 		return x.typ
 	case value:
@@ -362,7 +362,7 @@
 		}
 		fallthrough
 	default:
-		check.errorf(x.pos(), _NotAType, "%s is not a type", &x)
+		check.errorf(&x, _NotAType, "%s is not a type", &x)
 	}
 	return Typ[Invalid]
 }
@@ -375,7 +375,7 @@
 	check.expr(&x, e)
 	if x.mode != constant_ {
 		if x.mode != invalid {
-			check.errorf(x.pos(), _InvalidArrayLen, "array length %s must be constant", &x)
+			check.errorf(&x, _InvalidArrayLen, "array length %s must be constant", &x)
 		}
 		return -1
 	}
@@ -385,12 +385,12 @@
 				if n, ok := constant.Int64Val(val); ok && n >= 0 {
 					return n
 				}
-				check.errorf(x.pos(), _InvalidArrayLen, "invalid array length %s", &x)
+				check.errorf(&x, _InvalidArrayLen, "invalid array length %s", &x)
 				return -1
 			}
 		}
 	}
-	check.errorf(x.pos(), _InvalidArrayLen, "array length %s must be integer", &x)
+	check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x)
 	return -1
 }
 
@@ -407,7 +407,7 @@
 			if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
 				variadic = true
 			} else {
-				check.softErrorf(t.Pos(), _MisplacedDotDotDot, "can only use ... with final parameter in list")
+				check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list")
 				// ignore ... and continue
 			}
 		}
@@ -418,7 +418,7 @@
 			// named parameter
 			for _, name := range field.Names {
 				if name.Name == "" {
-					check.invalidAST(name.Pos(), "anonymous parameter")
+					check.invalidAST(name, "anonymous parameter")
 					// ok to continue
 				}
 				par := NewParam(name.Pos(), check.pkg, name.Name, typ)
@@ -436,7 +436,7 @@
 	}
 
 	if named && anonymous {
-		check.invalidAST(list.Pos(), "list contains both named and anonymous parameters")
+		check.invalidAST(list, "list contains both named and anonymous parameters")
 		// ok to continue
 	}
 
@@ -454,7 +454,7 @@
 
 func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
 	if alt := oset.insert(obj); alt != nil {
-		check.errorf(pos, _DuplicateDecl, "%s redeclared", obj.Name())
+		check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
 		check.reportAltDecl(alt)
 		return false
 	}
@@ -469,7 +469,7 @@
 			// and we don't care if a constructed AST has more.)
 			name := f.Names[0]
 			if name.Name == "_" {
-				check.errorf(name.Pos(), _BlankIfaceMethod, "invalid method name _")
+				check.errorf(name, _BlankIfaceMethod, "invalid method name _")
 				continue // ignore
 			}
 
@@ -477,7 +477,7 @@
 			sig, _ := typ.(*Signature)
 			if sig == nil {
 				if typ != Typ[Invalid] {
-					check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
+					check.invalidAST(f.Type, "%s is not a method signature", typ)
 				}
 				continue // ignore
 			}
@@ -501,7 +501,7 @@
 			utyp := check.underlying(typ)
 			if _, ok := utyp.(*Interface); !ok {
 				if utyp != Typ[Invalid] {
-					check.errorf(f.Type.Pos(), _InvalidIfaceEmbed, "%s is not an interface", typ)
+					check.errorf(f.Type, _InvalidIfaceEmbed, "%s is not an interface", typ)
 				}
 				continue
 			}
@@ -575,14 +575,14 @@
 			methods = append(methods, m)
 			mpos[m] = pos
 		case explicit:
-			check.errorf(pos, _DuplicateDecl, "duplicate method %s", m.name)
-			check.errorf(mpos[other.(*Func)], _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
+			check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+			check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
 		default:
 			// check method signatures after all types are computed (issue #33656)
 			check.atEnd(func() {
 				if !check.identical(m.typ, other.Type()) {
-					check.errorf(pos, _DuplicateDecl, "duplicate method %s", m.name)
-					check.errorf(mpos[other.(*Func)], _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
+					check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+					check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
 				}
 			})
 		}
@@ -641,7 +641,7 @@
 				return val
 			}
 		}
-		check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+		check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
 	}
 	return ""
 }
@@ -704,7 +704,7 @@
 			pos := f.Type.Pos()
 			name := embeddedFieldIdent(f.Type)
 			if name == nil {
-				check.invalidAST(pos, "embedded field type %s has no name", f.Type)
+				check.invalidAST(f.Type, "embedded field type %s has no name", f.Type)
 				name = ast.NewIdent("_")
 				name.NamePos = pos
 				addInvalid(name, pos)
@@ -723,19 +723,19 @@
 
 				// unsafe.Pointer is treated like a regular pointer
 				if t.kind == UnsafePointer {
-					check.errorf(pos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
+					check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
 					addInvalid(name, pos)
 					continue
 				}
 
 			case *Pointer:
-				check.errorf(pos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
+				check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
 				addInvalid(name, pos)
 				continue
 
 			case *Interface:
 				if isPtr {
-					check.errorf(pos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
+					check.errorf(f.Type, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
 					addInvalid(name, pos)
 					continue
 				}