]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/types/errors.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[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         "strconv"
15         "strings"
16 )
17
18 func assert(p bool) {
19         if !p {
20                 panic("assertion failed")
21         }
22 }
23
24 func unreachable() {
25         panic("unreachable")
26 }
27
28 // An error_ represents a type-checking error.
29 // To report an error_, call Checker.report.
30 type error_ struct {
31         desc []errorDesc
32         code errorCode
33         soft bool // TODO(gri) eventually determine this from an error code
34 }
35
36 // An errorDesc describes part of a type-checking error.
37 type errorDesc struct {
38         posn   positioner
39         format string
40         args   []interface{}
41 }
42
43 func (err *error_) empty() bool {
44         return err.desc == nil
45 }
46
47 func (err *error_) pos() token.Pos {
48         if err.empty() {
49                 return token.NoPos
50         }
51         return err.desc[0].posn.Pos()
52 }
53
54 func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
55         if err.empty() {
56                 return "no error"
57         }
58         var buf bytes.Buffer
59         for i := range err.desc {
60                 p := &err.desc[i]
61                 if i > 0 {
62                         fmt.Fprint(&buf, "\n\t")
63                         if p.posn.Pos().IsValid() {
64                                 fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
65                         }
66                 }
67                 buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
68         }
69         return buf.String()
70 }
71
72 // String is for testing.
73 func (err *error_) String() string {
74         if err.empty() {
75                 return "no error"
76         }
77         return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
78 }
79
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})
84 }
85
86 func (check *Checker) qualifier(pkg *Package) string {
87         // Qualify the package unless it's the package being type-checked.
88         if pkg != check.pkg {
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)
93                 }
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)
97                 }
98                 return pkg.name
99         }
100         return ""
101 }
102
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] {
107                 return
108         }
109         check.seenPkgMap[pkg] = true
110
111         forName, ok := check.pkgPathMap[pkg.name]
112         if !ok {
113                 forName = make(map[string]bool)
114                 check.pkgPathMap[pkg.name] = forName
115         }
116         forName[pkg.path] = true
117
118         for _, imp := range pkg.imports {
119                 check.markImports(imp)
120         }
121 }
122
123 // check may be nil.
124 func (check *Checker) sprintf(format string, args ...any) string {
125         var fset *token.FileSet
126         var qf Qualifier
127         if check != nil {
128                 fset = check.fset
129                 qf = check.qualifier
130         }
131         return sprintf(fset, qf, false, format, args...)
132 }
133
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) {
137                 case nil:
138                         arg = "<nil>"
139                 case operand:
140                         panic("got operand instead of *operand")
141                 case *operand:
142                         arg = operandString(a, qf)
143                 case token.Pos:
144                         if fset != nil {
145                                 arg = fset.Position(a).String()
146                         }
147                 case ast.Expr:
148                         arg = ExprString(a)
149                 case []ast.Expr:
150                         var buf bytes.Buffer
151                         buf.WriteByte('[')
152                         writeExprList(&buf, a)
153                         buf.WriteByte(']')
154                         arg = buf.String()
155                 case Object:
156                         arg = ObjectString(a, qf)
157                 case Type:
158                         arg = typeString(a, qf, debug)
159                 case []Type:
160                         var buf bytes.Buffer
161                         buf.WriteByte('[')
162                         for i, x := range a {
163                                 if i > 0 {
164                                         buf.WriteString(", ")
165                                 }
166                                 buf.WriteString(typeString(x, qf, debug))
167                         }
168                         buf.WriteByte(']')
169                         arg = buf.String()
170                 case []*TypeParam:
171                         var buf bytes.Buffer
172                         buf.WriteByte('[')
173                         for i, x := range a {
174                                 if i > 0 {
175                                         buf.WriteString(", ")
176                                 }
177                                 buf.WriteString(typeString(x, qf, debug)) // use typeString so we get subscripts when debugging
178                         }
179                         buf.WriteByte(']')
180                         arg = buf.String()
181                 }
182                 args[i] = arg
183         }
184         return fmt.Sprintf(format, args...)
185 }
186
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...),
192         )
193 }
194
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...))
198 }
199
200 // Report records the error pointed to by errp, setting check.firstError if
201 // necessary.
202 func (check *Checker) report(errp *error_) {
203         if errp.empty() {
204                 panic("empty error details")
205         }
206
207         span := spanOf(errp.desc[0].posn)
208         e := Error{
209                 Fset:       check.fset,
210                 Pos:        span.pos,
211                 Msg:        errp.msg(check.fset, check.qualifier),
212                 Soft:       errp.soft,
213                 go116code:  errp.code,
214                 go116start: span.start,
215                 go116end:   span.end,
216         }
217
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 {
225                 return
226         }
227
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)
235                 e.Pos = span.pos
236                 e.go116start = span.start
237                 e.go116end = span.end
238         }
239         err := e
240
241         if check.firstErr == nil {
242                 check.firstErr = err
243         }
244
245         if trace {
246                 pos := e.Pos
247                 msg := e.Msg
248                 check.trace(pos, "ERROR: %s", msg)
249         }
250
251         f := check.conf.Error
252         if f == nil {
253                 panic(bailout{}) // report only first error
254         }
255         f(err)
256 }
257
258 // newErrorf creates a new error_ for later reporting with check.report.
259 func newErrorf(at positioner, code errorCode, format string, args ...any) *error_ {
260         return &error_{
261                 desc: []errorDesc{{at, format, args}},
262                 code: code,
263         }
264 }
265
266 func (check *Checker) error(at positioner, code errorCode, msg string) {
267         check.report(newErrorf(at, code, msg))
268 }
269
270 func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
271         check.report(newErrorf(at, code, format, args...))
272 }
273
274 func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
275         err := newErrorf(at, code, format, args...)
276         err.soft = true
277         check.report(err)
278 }
279
280 func (check *Checker) invalidAST(at positioner, format string, args ...any) {
281         check.errorf(at, 0, "invalid AST: "+format, args...)
282 }
283
284 func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) {
285         check.errorf(at, code, "invalid argument: "+format, args...)
286 }
287
288 func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) {
289         check.errorf(at, code, "invalid operation: "+format, args...)
290 }
291
292 // The positioner interface is used to extract the position of type-checker
293 // errors.
294 type positioner interface {
295         Pos() token.Pos
296 }
297
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
305 }
306
307 func (e posSpan) Pos() token.Pos {
308         return e.pos
309 }
310
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()
316         if debug {
317                 assert(start <= pos && pos < end)
318         }
319         return posSpan{start, pos, end}
320 }
321
322 // atPos wraps a token.Pos to implement the positioner interface.
323 type atPos token.Pos
324
325 func (s atPos) Pos() token.Pos {
326         return token.Pos(s)
327 }
328
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) {
334         case nil:
335                 panic("nil positioner")
336         case posSpan:
337                 return x
338         case ast.Node:
339                 pos := x.Pos()
340                 return posSpan{pos, pos, x.End()}
341         case *operand:
342                 if x.expr != nil {
343                         pos := x.Pos()
344                         return posSpan{pos, pos, x.expr.End()}
345                 }
346                 return posSpan{token.NoPos, token.NoPos, token.NoPos}
347         default:
348                 pos := at.Pos()
349                 return posSpan{pos, pos, pos}
350         }
351 }
352
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
359                         b.WriteRune(r)
360                 }
361         }
362         if b.Len() < len(s) {
363                 return b.String()
364         }
365         return s
366 }