CompilerErrorMessages: true, // use error strings matching existing compiler errors
Error: func(err error) {
terr := err.(types2.Error)
- if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
- // types2 reports error clarifications via separate
- // error messages which are indented with a tab.
- // Ignore them to satisfy tools and tests that expect
- // only one error in such cases.
- // TODO(gri) Need to adjust error reporting in types2.
- return
- }
base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
},
Importer: &gcimports{
t.Error(err)
return
}
- // Ignore secondary error messages starting with "\t";
- // they are clarifying messages for a primary error.
- if !strings.Contains(err.Error(), ": \t") {
- errlist = append(errlist, err)
- }
+ errlist = append(errlist, err)
}
conf.Check(pkgName, files, nil)
"go/constant"
)
-func (check *Checker) reportAltDecl(obj Object) {
+func (err *error_) recordAltDecl(obj Object) {
if pos := obj.Pos(); pos.IsKnown() {
// We use "other" rather than "previous" here because
// the first declaration seen may not be textually
// earlier in the source.
- check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+ err.errorf(pos, "other declaration of %s", obj.Name())
}
}
// binding."
if obj.Name() != "_" {
if alt := scope.Insert(obj); alt != nil {
- check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
+ var err error_
+ err.errorf(obj, "%s redeclared in this block", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
return
}
obj.setScopePos(pos)
// cycle? That would be more consistent with other error messages.
i := firstInSrc(cycle)
obj := cycle[i]
+ var err error_
if check.conf.CompilerErrorMessages {
- check.errorf(obj.Pos(), "invalid recursive type %s", obj.Name())
+ err.errorf(obj, "invalid recursive type %s", obj.Name())
} else {
- check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name())
+ err.errorf(obj, "illegal cycle in declaration of %s", obj.Name())
}
for range cycle {
- check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ err.errorf(obj, "%s refers to", obj.Name())
i++
if i >= len(cycle) {
i = 0
}
obj = cycle[i]
}
- check.errorf(obj.Pos(), "\t%s", obj.Name())
+ err.errorf(obj, "%s", obj.Name())
+ check.report(&err)
}
// TODO(gri) This functionality should probably be with the Pos implementation.
// to it must be unique."
assert(m.name != "_")
if alt := mset.insert(m); alt != nil {
+ var err error_
switch alt.(type) {
case *Var:
- check.errorf(m.pos, "field and method with the same name %s", m.name)
+ err.errorf(m.pos, "field and method with the same name %s", m.name)
case *Func:
if check.conf.CompilerErrorMessages {
- check.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name)
+ err.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name)
} else {
- check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
+ err.errorf(m.pos, "method %s already declared for %s", m.name, obj)
}
default:
unreachable()
}
- check.reportAltDecl(alt)
+ err.recordAltDecl(alt)
+ check.report(&err)
continue
}
panic("unreachable")
}
-func (check *Checker) qualifier(pkg *Package) string {
- // Qualify the package unless it's the package being type-checked.
- if pkg != check.pkg {
- // If the same package name was used by multiple packages, display the full path.
- if check.pkgCnt[pkg.name] > 1 {
- return strconv.Quote(pkg.path)
+// An error_ represents a type-checking error.
+// To report an error_, call Checker.report.
+type error_ struct {
+ desc []errorDesc
+ soft bool // TODO(gri) eventually determine this from an error code
+}
+
+// An errorDesc describes part of a type-checking error.
+type errorDesc struct {
+ pos syntax.Pos
+ format string
+ args []interface{}
+}
+
+func (err *error_) empty() bool {
+ return err.desc == nil
+}
+
+func (err *error_) pos() syntax.Pos {
+ if err.empty() {
+ return nopos
+ }
+ return err.desc[0].pos
+}
+
+func (err *error_) msg(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.Fprintf(&buf, "\n\t%s: ", p.pos)
}
- return pkg.name
+ buf.WriteString(sprintf(qf, p.format, p.args...))
}
- return ""
+ return buf.String()
}
-func (check *Checker) sprintf(format string, args ...interface{}) string {
+// String is for testing.
+func (err *error_) String() string {
+ if err.empty() {
+ return "no error"
+ }
+ return fmt.Sprintf("%s: %s", err.pos(), err.msg(nil))
+}
+
+// errorf adds formatted error information to err.
+// It may be called multiple times to provide additional information.
+func (err *error_) errorf(at poser, format string, args ...interface{}) {
+ err.desc = append(err.desc, errorDesc{posFor(at), format, args})
+}
+
+func sprintf(qf Qualifier, format string, args ...interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
case operand:
panic("internal error: should always pass *operand")
case *operand:
- arg = operandString(a, check.qualifier)
+ arg = operandString(a, qf)
case syntax.Pos:
arg = a.String()
case syntax.Expr:
arg = syntax.String(a)
case Object:
- arg = ObjectString(a, check.qualifier)
+ arg = ObjectString(a, qf)
case Type:
- arg = TypeString(a, check.qualifier)
+ arg = TypeString(a, qf)
}
args[i] = arg
}
return fmt.Sprintf(format, args...)
}
+func (check *Checker) qualifier(pkg *Package) string {
+ // Qualify the package unless it's the package being type-checked.
+ if pkg != check.pkg {
+ // If the same package name was used by multiple packages, display the full path.
+ if check.pkgCnt[pkg.name] > 1 {
+ return strconv.Quote(pkg.path)
+ }
+ return pkg.name
+ }
+ return ""
+}
+
+func (check *Checker) sprintf(format string, args ...interface{}) string {
+ return sprintf(check.qualifier, format, args...)
+}
+
+func (check *Checker) report(err *error_) {
+ if err.empty() {
+ panic("internal error: reporting no error")
+ }
+ check.err(err.pos(), err.msg(check.qualifier), err.soft)
+}
+
func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) {
fmt.Printf("%s:\t%s%s\n",
pos,
import "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 = "<unknown position>: foo 42"
+ err.errorf(nopos, "foo %d", 42)
+ if got := err.String(); got != want {
+ t.Errorf("simple error: got %q, want %q", got, want)
+ }
+
+ want = "<unknown position>: foo 42\n\t<unknown position>: bar 43"
+ err.errorf(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 {
in, want string
// reportCycle reports an error for the given cycle.
func (check *Checker) reportCycle(cycle []Object) {
obj := cycle[0]
+ var err error_
if check.conf.CompilerErrorMessages {
- check.errorf(obj, "initialization loop for %s", obj.Name())
+ err.errorf(obj, "initialization loop for %s", obj.Name())
} else {
- check.errorf(obj, "initialization cycle for %s", obj.Name())
+ err.errorf(obj, "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, "\t%s refers to", obj.Name()) // secondary error, \t indented
+ err.errorf(obj, "%s refers to", obj.Name())
obj = cycle[i]
}
// print cycle[0] again to close the cycle
- check.errorf(obj, "\t%s", obj.Name())
+ err.errorf(obj, "%s", obj.Name())
+ check.report(&err)
}
// ----------------------------------------------------------------------------
if name := s.Label.Value; name != "_" {
lbl := NewLabel(s.Label.Pos(), check.pkg, name)
if alt := all.Insert(lbl); alt != nil {
- check.softErrorf(lbl.pos, "label %s already declared", name)
- check.reportAltDecl(alt)
+ var err error_
+ err.soft = true
+ err.errorf(lbl.pos, "label %s already declared", name)
+ err.recordAltDecl(alt)
+ check.report(&err)
// ok to continue
} else {
b.insert(s)
// the object may be imported into more than one file scope
// concurrently. See issue #32154.)
if alt := fileScope.Insert(obj); alt != nil {
- check.errorf(s.LocalPkgName, "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
+ var err error_
+ err.errorf(s.LocalPkgName, "%s redeclared in this block", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
} else {
check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
}
for _, scope := range fileScopes {
for _, obj := range scope.elems {
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ var err error_
if pkg, ok := obj.(*PkgName); ok {
- check.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())
- check.reportAltDecl(pkg)
+ err.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())
+ err.recordAltDecl(pkg)
} else {
- check.errorf(alt, "%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)
+ err.errorf(alt, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+ // TODO(gri) dot-imported objects don't have a position; recordAltDecl won't print anything
+ err.recordAltDecl(obj)
}
+ check.report(&err)
}
}
}
// (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, "duplicate case %s in expression switch", &v)
- check.error(vt.pos, "\tprevious case") // secondary error, \t indented
+ var err error_
+ err.errorf(&v, "duplicate case %s in expression switch", &v)
+ err.errorf(vt.pos, "previous case")
+ check.report(&err)
continue L
}
}
if T != nil {
Ts = T.String()
}
- check.errorf(e, "duplicate case %s in type switch", Ts)
- check.error(pos, "\tprevious case") // secondary error, \t indented
+ var err error_
+ err.errorf(e, "duplicate case %s in type switch", Ts)
+ err.errorf(pos, "previous case")
+ check.report(&err)
continue L
}
}
// 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, "result parameter %s not in scope at return", obj.name)
- check.errorf(alt, "\tinner declaration of %s", obj)
+ var err error_
+ err.errorf(s, "result parameter %s not in scope at return", obj.name)
+ err.errorf(alt, "inner declaration of %s", obj)
+ check.report(&err)
// ok to continue
}
}
params, variadic := check.collectParams(scope, ftyp.ParamList, nil, true)
results, _ := check.collectParams(scope, ftyp.ResultList, nil, false)
scope.Squash(func(obj, alt Object) {
- check.errorf(obj, "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
+ var err error_
+ err.errorf(obj, "%s redeclared in this block", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
})
if recvPar != nil {
func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
if alt := oset.insert(obj); alt != nil {
- check.errorf(pos, "%s redeclared", obj.Name())
- check.reportAltDecl(alt)
+ var err error_
+ err.errorf(pos, "%s redeclared", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
return false
}
return true
methods = append(methods, m)
mpos[m] = pos
case explicit:
- check.errorf(pos, "duplicate method %s", m.name)
- check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ var err error_
+ err.errorf(pos, "duplicate method %s", m.name)
+ err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
+ check.report(&err)
default:
// We have a duplicate method name in an embedded (not explicitly declared) method.
// Check method signatures after all types are computed (issue #33656).
// error message.
check.atEnd(func() {
if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
- check.errorf(pos, "duplicate method %s", m.name)
- check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ var err error_
+ err.errorf(pos, "duplicate method %s", m.name)
+ err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
+ check.report(&err)
}
})
}