]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/types/errors.go
all: fix misuses of "a" vs "an"
[gostls13.git] / src / go / types / errors.go
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.
4
5 // This file implements various error reporters.
6
7 package types
8
9 import (
10         "bytes"
11         "fmt"
12         "go/ast"
13         "go/token"
14         . "internal/types/errors"
15         "runtime"
16         "strconv"
17         "strings"
18 )
19
20 func assert(p bool) {
21         if !p {
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)
27                 }
28                 panic(msg)
29         }
30 }
31
32 func unreachable() {
33         panic("unreachable")
34 }
35
36 // An error_ represents a type-checking error.
37 // To report an error_, call Checker.report.
38 type error_ struct {
39         desc []errorDesc
40         code Code
41         soft bool // TODO(gri) eventually determine this from an error code
42 }
43
44 // An errorDesc describes part of a type-checking error.
45 type errorDesc struct {
46         posn   positioner
47         format string
48         args   []interface{}
49 }
50
51 func (err *error_) empty() bool {
52         return err.desc == nil
53 }
54
55 func (err *error_) pos() token.Pos {
56         if err.empty() {
57                 return nopos
58         }
59         return err.desc[0].posn.Pos()
60 }
61
62 func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
63         if err.empty() {
64                 return "no error"
65         }
66         var buf strings.Builder
67         for i := range err.desc {
68                 p := &err.desc[i]
69                 if i > 0 {
70                         fmt.Fprint(&buf, "\n\t")
71                         if p.posn.Pos().IsValid() {
72                                 fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
73                         }
74                 }
75                 buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
76         }
77         return buf.String()
78 }
79
80 // String is for testing.
81 func (err *error_) String() string {
82         if err.empty() {
83                 return "no error"
84         }
85         return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
86 }
87
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})
92 }
93
94 func (check *Checker) qualifier(pkg *Package) string {
95         // Qualify the package unless it's the package being type-checked.
96         if pkg != check.pkg {
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)
101                 }
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)
105                 }
106                 return pkg.name
107         }
108         return ""
109 }
110
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] {
115                 return
116         }
117         check.seenPkgMap[pkg] = true
118
119         forName, ok := check.pkgPathMap[pkg.name]
120         if !ok {
121                 forName = make(map[string]bool)
122                 check.pkgPathMap[pkg.name] = forName
123         }
124         forName[pkg.path] = true
125
126         for _, imp := range pkg.imports {
127                 check.markImports(imp)
128         }
129 }
130
131 // check may be nil.
132 func (check *Checker) sprintf(format string, args ...any) string {
133         var fset *token.FileSet
134         var qf Qualifier
135         if check != nil {
136                 fset = check.fset
137                 qf = check.qualifier
138         }
139         return sprintf(fset, qf, false, format, args...)
140 }
141
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) {
145                 case nil:
146                         arg = "<nil>"
147                 case operand:
148                         panic("got operand instead of *operand")
149                 case *operand:
150                         arg = operandString(a, qf)
151                 case token.Pos:
152                         if fset != nil {
153                                 arg = fset.Position(a).String()
154                         }
155                 case ast.Expr:
156                         arg = ExprString(a)
157                 case []ast.Expr:
158                         var buf bytes.Buffer
159                         buf.WriteByte('[')
160                         writeExprList(&buf, a)
161                         buf.WriteByte(']')
162                         arg = buf.String()
163                 case Object:
164                         arg = ObjectString(a, qf)
165                 case Type:
166                         var buf bytes.Buffer
167                         w := newTypeWriter(&buf, qf)
168                         w.tpSubscripts = tpSubscripts
169                         w.typ(a)
170                         arg = buf.String()
171                 case []Type:
172                         var buf bytes.Buffer
173                         w := newTypeWriter(&buf, qf)
174                         w.tpSubscripts = tpSubscripts
175                         buf.WriteByte('[')
176                         for i, x := range a {
177                                 if i > 0 {
178                                         buf.WriteString(", ")
179                                 }
180                                 w.typ(x)
181                         }
182                         buf.WriteByte(']')
183                         arg = buf.String()
184                 case []*TypeParam:
185                         var buf bytes.Buffer
186                         w := newTypeWriter(&buf, qf)
187                         w.tpSubscripts = tpSubscripts
188                         buf.WriteByte('[')
189                         for i, x := range a {
190                                 if i > 0 {
191                                         buf.WriteString(", ")
192                                 }
193                                 w.typ(x)
194                         }
195                         buf.WriteByte(']')
196                         arg = buf.String()
197                 }
198                 args[i] = arg
199         }
200         return fmt.Sprintf(format, args...)
201 }
202
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...),
208         )
209 }
210
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...))
214 }
215
216 // Report records the error pointed to by errp, setting check.firstError if
217 // necessary.
218 func (check *Checker) report(errp *error_) {
219         if errp.empty() {
220                 panic("empty error details")
221         }
222
223         msg := errp.msg(check.fset, check.qualifier)
224         switch errp.code {
225         case InvalidSyntaxTree:
226                 msg = "invalid AST: " + msg
227         case 0:
228                 panic("no error code provided")
229         }
230
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:]
236                 } else {
237                         msg += u
238                 }
239         }
240
241         span := spanOf(errp.desc[0].posn)
242         e := Error{
243                 Fset:       check.fset,
244                 Pos:        span.pos,
245                 Msg:        msg,
246                 Soft:       errp.soft,
247                 go116code:  errp.code,
248                 go116start: span.start,
249                 go116end:   span.end,
250         }
251
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 {
259                 return
260         }
261
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)
269                 e.Pos = span.pos
270                 e.go116start = span.start
271                 e.go116end = span.end
272         }
273         err := e
274
275         if check.firstErr == nil {
276                 check.firstErr = err
277         }
278
279         if check.conf._Trace {
280                 pos := e.Pos
281                 msg := e.Msg
282                 check.trace(pos, "ERROR: %s", msg)
283         }
284
285         f := check.conf.Error
286         if f == nil {
287                 panic(bailout{}) // report only first error
288         }
289         f(err)
290 }
291
292 const (
293         invalidArg = "invalid argument: "
294         invalidOp  = "invalid operation: "
295 )
296
297 // newErrorf creates a new error_ for later reporting with check.report.
298 func newErrorf(at positioner, code Code, format string, args ...any) *error_ {
299         return &error_{
300                 desc: []errorDesc{{at, format, args}},
301                 code: code,
302         }
303 }
304
305 func (check *Checker) error(at positioner, code Code, msg string) {
306         check.report(newErrorf(at, code, "%s", msg))
307 }
308
309 func (check *Checker) errorf(at positioner, code Code, format string, args ...any) {
310         check.report(newErrorf(at, code, format, args...))
311 }
312
313 func (check *Checker) softErrorf(at positioner, code Code, format string, args ...any) {
314         err := newErrorf(at, code, format, args...)
315         err.soft = true
316         check.report(err)
317 }
318
319 func (check *Checker) versionErrorf(at positioner, v version, format string, args ...interface{}) {
320         msg := check.sprintf(format, args...)
321         var err *error_
322         err = newErrorf(at, UnsupportedFeature, "%s requires %s or later", msg, v)
323         check.report(err)
324 }
325
326 // The positioner interface is used to extract the position of type-checker
327 // errors.
328 type positioner interface {
329         Pos() token.Pos
330 }
331
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
339 }
340
341 func (e posSpan) Pos() token.Pos {
342         return e.pos
343 }
344
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()
350         if debug {
351                 assert(start <= pos && pos < end)
352         }
353         return posSpan{start, pos, end}
354 }
355
356 // atPos wraps a token.Pos to implement the positioner interface.
357 type atPos token.Pos
358
359 func (s atPos) Pos() token.Pos {
360         return token.Pos(s)
361 }
362
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) {
368         case nil:
369                 panic("nil positioner")
370         case posSpan:
371                 return x
372         case ast.Node:
373                 pos := x.Pos()
374                 return posSpan{pos, pos, x.End()}
375         case *operand:
376                 if x.expr != nil {
377                         pos := x.Pos()
378                         return posSpan{pos, pos, x.expr.End()}
379                 }
380                 return posSpan{nopos, nopos, nopos}
381         default:
382                 pos := at.Pos()
383                 return posSpan{pos, pos, pos}
384         }
385 }
386
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
393                         buf.WriteRune(r)
394                 }
395         }
396         if buf.Len() < len(s) {
397                 return buf.String()
398         }
399         return s
400 }