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.
14 . "internal/types/errors"
22 msg := "assertion failed"
23 // Include information about the assertion location. Due to panic recovery,
24 // this location is otherwise buried in the middle of the panicking stack.
25 if _, file, line, ok := runtime.Caller(1); ok {
26 msg = fmt.Sprintf("%s:%d: %s", file, line, msg)
36 // An error_ represents a type-checking error.
37 // To report an error_, call Checker.report.
41 soft bool // TODO(gri) eventually determine this from an error code
44 // An errorDesc describes part of a type-checking error.
45 type errorDesc struct {
51 func (err *error_) empty() bool {
52 return err.desc == nil
55 func (err *error_) pos() token.Pos {
59 return err.desc[0].posn.Pos()
62 func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
66 var buf strings.Builder
67 for i := range err.desc {
70 fmt.Fprint(&buf, "\n\t")
71 if p.posn.Pos().IsValid() {
72 fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
75 buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
80 // String is for testing.
81 func (err *error_) String() string {
85 return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
88 // errorf adds formatted error information to err.
89 // It may be called multiple times to provide additional information.
90 func (err *error_) errorf(at token.Pos, format string, args ...interface{}) {
91 err.desc = append(err.desc, errorDesc{atPos(at), format, args})
94 func (check *Checker) qualifier(pkg *Package) string {
95 // Qualify the package unless it's the package being type-checked.
97 if check.pkgPathMap == nil {
98 check.pkgPathMap = make(map[string]map[string]bool)
99 check.seenPkgMap = make(map[*Package]bool)
100 check.markImports(check.pkg)
102 // If the same package name was used by multiple packages, display the full path.
103 if len(check.pkgPathMap[pkg.name]) > 1 {
104 return strconv.Quote(pkg.path)
111 // markImports recursively walks pkg and its imports, to record unique import
112 // paths in pkgPathMap.
113 func (check *Checker) markImports(pkg *Package) {
114 if check.seenPkgMap[pkg] {
117 check.seenPkgMap[pkg] = true
119 forName, ok := check.pkgPathMap[pkg.name]
121 forName = make(map[string]bool)
122 check.pkgPathMap[pkg.name] = forName
124 forName[pkg.path] = true
126 for _, imp := range pkg.imports {
127 check.markImports(imp)
132 func (check *Checker) sprintf(format string, args ...any) string {
133 var fset *token.FileSet
139 return sprintf(fset, qf, false, format, args...)
142 func sprintf(fset *token.FileSet, qf Qualifier, tpSubscripts bool, format string, args ...any) string {
143 for i, arg := range args {
144 switch a := arg.(type) {
148 panic("got operand instead of *operand")
150 arg = operandString(a, qf)
153 arg = fset.Position(a).String()
160 writeExprList(&buf, a)
164 arg = ObjectString(a, qf)
167 w := newTypeWriter(&buf, qf)
168 w.tpSubscripts = tpSubscripts
173 w := newTypeWriter(&buf, qf)
174 w.tpSubscripts = tpSubscripts
176 for i, x := range a {
178 buf.WriteString(", ")
186 w := newTypeWriter(&buf, qf)
187 w.tpSubscripts = tpSubscripts
189 for i, x := range a {
191 buf.WriteString(", ")
200 return fmt.Sprintf(format, args...)
203 func (check *Checker) trace(pos token.Pos, format string, args ...any) {
204 fmt.Printf("%s:\t%s%s\n",
205 check.fset.Position(pos),
206 strings.Repeat(". ", check.indent),
207 sprintf(check.fset, check.qualifier, true, format, args...),
211 // dump is only needed for debugging
212 func (check *Checker) dump(format string, args ...any) {
213 fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
216 // Report records the error pointed to by errp, setting check.firstError if
218 func (check *Checker) report(errp *error_) {
220 panic("empty error details")
223 msg := errp.msg(check.fset, check.qualifier)
225 case InvalidSyntaxTree:
226 msg = "invalid AST: " + msg
228 panic("no error code provided")
231 // If we have a URL for error codes, add a link to the first line.
232 if errp.code != 0 && check.conf._ErrorURL != "" {
233 u := fmt.Sprintf(check.conf._ErrorURL, errp.code)
234 if i := strings.Index(msg, "\n"); i >= 0 {
235 msg = msg[:i] + u + msg[i:]
241 span := spanOf(errp.desc[0].posn)
247 go116code: errp.code,
248 go116start: span.start,
252 // Cheap trick: Don't report errors with messages containing
253 // "invalid operand" or "invalid type" as those tend to be
254 // follow-on errors which don't add useful information. Only
255 // exclude them if these strings are not at the beginning,
256 // and only if we have at least one error already reported.
257 isInvalidErr := strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0
258 if check.firstErr != nil && isInvalidErr {
262 e.Msg = stripAnnotations(e.Msg)
263 if check.errpos != nil {
264 // If we have an internal error and the errpos override is set, use it to
265 // augment our error positioning.
266 // TODO(rFindley) we may also want to augment the error message and refer
267 // to the position (pos) in the original expression.
268 span := spanOf(check.errpos)
270 e.go116start = span.start
271 e.go116end = span.end
275 if check.firstErr == nil {
279 if check.conf._Trace {
282 check.trace(pos, "ERROR: %s", msg)
285 f := check.conf.Error
287 panic(bailout{}) // report only first error
293 invalidArg = "invalid argument: "
294 invalidOp = "invalid operation: "
297 // newErrorf creates a new error_ for later reporting with check.report.
298 func newErrorf(at positioner, code Code, format string, args ...any) *error_ {
300 desc: []errorDesc{{at, format, args}},
305 func (check *Checker) error(at positioner, code Code, msg string) {
306 check.report(newErrorf(at, code, "%s", msg))
309 func (check *Checker) errorf(at positioner, code Code, format string, args ...any) {
310 check.report(newErrorf(at, code, format, args...))
313 func (check *Checker) softErrorf(at positioner, code Code, format string, args ...any) {
314 err := newErrorf(at, code, format, args...)
319 func (check *Checker) versionErrorf(at positioner, v version, format string, args ...interface{}) {
320 msg := check.sprintf(format, args...)
322 err = newErrorf(at, UnsupportedFeature, "%s requires %s or later", msg, v)
326 // The positioner interface is used to extract the position of type-checker
328 type positioner interface {
332 // posSpan holds a position range along with a highlighted position within that
333 // range. This is used for positioning errors, with pos by convention being the
334 // first position in the source where the error is known to exist, and start
335 // and end defining the full span of syntax being considered when the error was
336 // detected. Invariant: start <= pos < end || start == pos == end.
337 type posSpan struct {
338 start, pos, end token.Pos
341 func (e posSpan) Pos() token.Pos {
345 // inNode creates a posSpan for the given node.
346 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
347 // first byte after node within the source).
348 func inNode(node ast.Node, pos token.Pos) posSpan {
349 start, end := node.Pos(), node.End()
351 assert(start <= pos && pos < end)
353 return posSpan{start, pos, end}
356 // atPos wraps a token.Pos to implement the positioner interface.
359 func (s atPos) Pos() token.Pos {
363 // spanOf extracts an error span from the given positioner. By default this is
364 // the trivial span starting and ending at pos, but this span is expanded when
365 // the argument naturally corresponds to a span of source code.
366 func spanOf(at positioner) posSpan {
367 switch x := at.(type) {
369 panic("nil positioner")
374 return posSpan{pos, pos, x.End()}
378 return posSpan{pos, pos, x.expr.End()}
380 return posSpan{nopos, nopos, nopos}
383 return posSpan{pos, pos, pos}
387 // stripAnnotations removes internal (type) annotations from s.
388 func stripAnnotations(s string) string {
389 var buf strings.Builder
390 for _, r := range s {
391 // strip #'s and subscript digits
392 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
396 if buf.Len() < len(s) {