1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements various error reporters.
20 panic("assertion failed")
28 // An error_ represents a type-checking error.
29 // To report an error_, call Checker.report.
33 soft bool // TODO(gri) eventually determine this from an error code
36 // An errorDesc describes part of a type-checking error.
37 type errorDesc struct {
43 func (err *error_) empty() bool {
44 return err.desc == nil
47 func (err *error_) pos() token.Pos {
51 return err.desc[0].posn.Pos()
54 func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
59 for i := range err.desc {
62 fmt.Fprint(&buf, "\n\t")
63 if p.posn.Pos().IsValid() {
64 fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
67 buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
72 // String is for testing.
73 func (err *error_) String() string {
77 return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
80 // errorf adds formatted error information to err.
81 // It may be called multiple times to provide additional information.
82 func (err *error_) errorf(at token.Pos, format string, args ...interface{}) {
83 err.desc = append(err.desc, errorDesc{atPos(at), format, args})
86 func (check *Checker) qualifier(pkg *Package) string {
87 // Qualify the package unless it's the package being type-checked.
89 if check.pkgPathMap == nil {
90 check.pkgPathMap = make(map[string]map[string]bool)
91 check.seenPkgMap = make(map[*Package]bool)
92 check.markImports(check.pkg)
94 // If the same package name was used by multiple packages, display the full path.
95 if len(check.pkgPathMap[pkg.name]) > 1 {
96 return strconv.Quote(pkg.path)
103 // markImports recursively walks pkg and its imports, to record unique import
104 // paths in pkgPathMap.
105 func (check *Checker) markImports(pkg *Package) {
106 if check.seenPkgMap[pkg] {
109 check.seenPkgMap[pkg] = true
111 forName, ok := check.pkgPathMap[pkg.name]
113 forName = make(map[string]bool)
114 check.pkgPathMap[pkg.name] = forName
116 forName[pkg.path] = true
118 for _, imp := range pkg.imports {
119 check.markImports(imp)
124 func (check *Checker) sprintf(format string, args ...any) string {
125 var fset *token.FileSet
131 return sprintf(fset, qf, false, format, args...)
134 func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {
135 for i, arg := range args {
136 switch a := arg.(type) {
140 panic("got operand instead of *operand")
142 arg = operandString(a, qf)
145 arg = fset.Position(a).String()
152 writeExprList(&buf, a)
156 arg = ObjectString(a, qf)
158 arg = typeString(a, qf, debug)
162 for i, x := range a {
164 buf.WriteString(", ")
166 buf.WriteString(typeString(x, qf, debug))
173 for i, x := range a {
175 buf.WriteString(", ")
177 buf.WriteString(typeString(x, qf, debug)) // use typeString so we get subscripts when debugging
184 return fmt.Sprintf(format, args...)
187 func (check *Checker) trace(pos token.Pos, format string, args ...any) {
188 fmt.Printf("%s:\t%s%s\n",
189 check.fset.Position(pos),
190 strings.Repeat(". ", check.indent),
191 sprintf(check.fset, check.qualifier, true, format, args...),
195 // dump is only needed for debugging
196 func (check *Checker) dump(format string, args ...any) {
197 fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
200 // Report records the error pointed to by errp, setting check.firstError if
202 func (check *Checker) report(errp *error_) {
204 panic("empty error details")
207 span := spanOf(errp.desc[0].posn)
211 Msg: errp.msg(check.fset, check.qualifier),
213 go116code: errp.code,
214 go116start: span.start,
218 // Cheap trick: Don't report errors with messages containing
219 // "invalid operand" or "invalid type" as those tend to be
220 // follow-on errors which don't add useful information. Only
221 // exclude them if these strings are not at the beginning,
222 // and only if we have at least one error already reported.
223 isInvalidErr := strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0
224 if check.firstErr != nil && isInvalidErr {
228 e.Msg = stripAnnotations(e.Msg)
229 if check.errpos != nil {
230 // If we have an internal error and the errpos override is set, use it to
231 // augment our error positioning.
232 // TODO(rFindley) we may also want to augment the error message and refer
233 // to the position (pos) in the original expression.
234 span := spanOf(check.errpos)
236 e.go116start = span.start
237 e.go116end = span.end
241 if check.firstErr == nil {
248 check.trace(pos, "ERROR: %s", msg)
251 f := check.conf.Error
253 panic(bailout{}) // report only first error
258 // newErrorf creates a new error_ for later reporting with check.report.
259 func newErrorf(at positioner, code errorCode, format string, args ...any) *error_ {
261 desc: []errorDesc{{at, format, args}},
266 func (check *Checker) error(at positioner, code errorCode, msg string) {
267 check.report(newErrorf(at, code, msg))
270 func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
271 check.report(newErrorf(at, code, format, args...))
274 func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
275 err := newErrorf(at, code, format, args...)
280 func (check *Checker) invalidAST(at positioner, format string, args ...any) {
281 check.errorf(at, 0, "invalid AST: "+format, args...)
284 func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) {
285 check.errorf(at, code, "invalid argument: "+format, args...)
288 func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) {
289 check.errorf(at, code, "invalid operation: "+format, args...)
292 // The positioner interface is used to extract the position of type-checker
294 type positioner interface {
298 // posSpan holds a position range along with a highlighted position within that
299 // range. This is used for positioning errors, with pos by convention being the
300 // first position in the source where the error is known to exist, and start
301 // and end defining the full span of syntax being considered when the error was
302 // detected. Invariant: start <= pos < end || start == pos == end.
303 type posSpan struct {
304 start, pos, end token.Pos
307 func (e posSpan) Pos() token.Pos {
311 // inNode creates a posSpan for the given node.
312 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
313 // first byte after node within the source).
314 func inNode(node ast.Node, pos token.Pos) posSpan {
315 start, end := node.Pos(), node.End()
317 assert(start <= pos && pos < end)
319 return posSpan{start, pos, end}
322 // atPos wraps a token.Pos to implement the positioner interface.
325 func (s atPos) Pos() token.Pos {
329 // spanOf extracts an error span from the given positioner. By default this is
330 // the trivial span starting and ending at pos, but this span is expanded when
331 // the argument naturally corresponds to a span of source code.
332 func spanOf(at positioner) posSpan {
333 switch x := at.(type) {
335 panic("nil positioner")
340 return posSpan{pos, pos, x.End()}
344 return posSpan{pos, pos, x.expr.End()}
346 return posSpan{token.NoPos, token.NoPos, token.NoPos}
349 return posSpan{pos, pos, pos}
353 // stripAnnotations removes internal (type) annotations from s.
354 func stripAnnotations(s string) string {
355 var b strings.Builder
356 for _, r := range s {
357 // strip #'s and subscript digits
358 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
362 if b.Len() < len(s) {