1
0
mirror of https://github.com/golang/go synced 2024-11-16 21:14:44 -07:00

go/types: introduce the error_ type to match types2

To begin aligning with types2 error reporting, use an error_ type to
hold unevaluated error information, to report via Checker.report.

Change-Id: Ic5ac515759961e55b81acc9eeaac4db25b61804c
Reviewed-on: https://go-review.googlesource.com/c/go/+/400824
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Findley 2022-04-18 17:54:40 -04:00
parent 1899472647
commit 65d7345e8b
2 changed files with 108 additions and 22 deletions

View File

@ -25,6 +25,64 @@ func unreachable() {
panic("unreachable")
}
// An error_ represents a type-checking error.
// To report an error_, call Checker.report.
type error_ struct {
desc []errorDesc
code errorCode
soft bool // TODO(gri) eventually determine this from an error code
}
// An errorDesc describes part of a type-checking error.
type errorDesc struct {
posn positioner
format string
args []interface{}
}
func (err *error_) empty() bool {
return err.desc == nil
}
func (err *error_) pos() token.Pos {
if err.empty() {
return token.NoPos
}
return err.desc[0].posn.Pos()
}
func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
if err.empty() {
return "no error"
}
var buf bytes.Buffer
for i := range err.desc {
p := &err.desc[i]
if i > 0 {
fmt.Fprint(&buf, "\n\t")
if p.posn.Pos().IsValid() {
fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
}
}
buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
}
return buf.String()
}
// String is for testing.
func (err *error_) String() string {
if err.empty() {
return "no error"
}
return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
}
// errorf adds formatted error information to err.
// It may be called multiple times to provide additional information.
func (err *error_) errorf(at token.Pos, format string, args ...interface{}) {
err.desc = append(err.desc, errorDesc{atPos(at), format, args})
}
func (check *Checker) qualifier(pkg *Package) string {
// Qualify the package unless it's the package being type-checked.
if pkg != check.pkg {
@ -141,8 +199,22 @@ func (check *Checker) dump(format string, args ...any) {
// Report records the error pointed to by errp, setting check.firstError if
// necessary.
func (check *Checker) report(errp *Error) {
e := *errp
func (check *Checker) report(errp *error_) {
if errp.empty() {
panic("empty error details")
}
span := spanOf(errp.desc[0].posn)
e := Error{
Fset: check.fset,
Pos: span.pos,
Msg: errp.msg(check.fset, check.qualifier),
Soft: errp.soft,
go116code: errp.code,
go116start: span.start,
go116end: span.end,
}
// Cheap trick: Don't report errors with messages containing
// "invalid operand" or "invalid type" as those tend to be
// follow-on errors which don't add useful information. Only
@ -183,35 +255,26 @@ func (check *Checker) report(errp *Error) {
f(err)
}
func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) *Error {
span := spanOf(at)
return &Error{
Fset: check.fset,
Pos: span.pos,
Msg: msg,
Soft: soft,
go116code: code,
go116start: span.start,
go116end: span.end,
// newErrorf creates a new error_ for later reporting with check.report.
func newErrorf(at positioner, code errorCode, format string, args ...any) *error_ {
return &error_{
desc: []errorDesc{{at, format, args}},
code: code,
}
}
// newErrorf creates a new Error, but does not handle it.
func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...any) *Error {
msg := check.sprintf(format, args...)
return check.newError(at, code, soft, msg)
}
func (check *Checker) error(at positioner, code errorCode, msg string) {
check.report(check.newError(at, code, false, msg))
check.report(newErrorf(at, code, msg))
}
func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
check.error(at, code, check.sprintf(format, args...))
check.report(newErrorf(at, code, format, args...))
}
func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
check.report(check.newErrorf(at, code, true, format, args...))
err := newErrorf(at, code, format, args...)
err.soft = true
check.report(err)
}
func (check *Checker) invalidAST(at positioner, format string, args ...any) {

View File

@ -4,7 +4,30 @@
package types
import "testing"
import (
"go/token"
"testing"
)
func TestError(t *testing.T) {
var err error_
want := "no error"
if got := err.String(); got != want {
t.Errorf("empty error: got %q, want %q", got, want)
}
want = "0: foo 42"
err.errorf(token.NoPos, "foo %d", 42)
if got := err.String(); got != want {
t.Errorf("simple error: got %q, want %q", got, want)
}
want = "0: foo 42\n\tbar 43"
err.errorf(token.NoPos, "bar %d", 43)
if got := err.String(); got != want {
t.Errorf("simple error: got %q, want %q", got, want)
}
}
func TestStripAnnotations(t *testing.T) {
for _, test := range []struct {