]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/types/errors.go
go/types, types2: unify core types for unbound type parameters
[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         "errors"
12         "fmt"
13         "go/ast"
14         "go/token"
15         "strconv"
16         "strings"
17 )
18
19 func assert(p bool) {
20         if !p {
21                 panic("assertion failed")
22         }
23 }
24
25 func unreachable() {
26         panic("unreachable")
27 }
28
29 func (check *Checker) qualifier(pkg *Package) string {
30         // Qualify the package unless it's the package being type-checked.
31         if pkg != check.pkg {
32                 if check.pkgPathMap == nil {
33                         check.pkgPathMap = make(map[string]map[string]bool)
34                         check.seenPkgMap = make(map[*Package]bool)
35                         check.markImports(check.pkg)
36                 }
37                 // If the same package name was used by multiple packages, display the full path.
38                 if len(check.pkgPathMap[pkg.name]) > 1 {
39                         return strconv.Quote(pkg.path)
40                 }
41                 return pkg.name
42         }
43         return ""
44 }
45
46 // markImports recursively walks pkg and its imports, to record unique import
47 // paths in pkgPathMap.
48 func (check *Checker) markImports(pkg *Package) {
49         if check.seenPkgMap[pkg] {
50                 return
51         }
52         check.seenPkgMap[pkg] = true
53
54         forName, ok := check.pkgPathMap[pkg.name]
55         if !ok {
56                 forName = make(map[string]bool)
57                 check.pkgPathMap[pkg.name] = forName
58         }
59         forName[pkg.path] = true
60
61         for _, imp := range pkg.imports {
62                 check.markImports(imp)
63         }
64 }
65
66 // check may be nil.
67 func (check *Checker) sprintf(format string, args ...any) string {
68         var fset *token.FileSet
69         var qf Qualifier
70         if check != nil {
71                 fset = check.fset
72                 qf = check.qualifier
73         }
74         return sprintf(fset, qf, false, format, args...)
75 }
76
77 func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {
78         for i, arg := range args {
79                 switch a := arg.(type) {
80                 case nil:
81                         arg = "<nil>"
82                 case operand:
83                         panic("got operand instead of *operand")
84                 case *operand:
85                         arg = operandString(a, qf)
86                 case token.Pos:
87                         if fset != nil {
88                                 arg = fset.Position(a).String()
89                         }
90                 case ast.Expr:
91                         arg = ExprString(a)
92                 case []ast.Expr:
93                         var buf bytes.Buffer
94                         buf.WriteByte('[')
95                         writeExprList(&buf, a)
96                         buf.WriteByte(']')
97                         arg = buf.String()
98                 case Object:
99                         arg = ObjectString(a, qf)
100                 case Type:
101                         arg = typeString(a, qf, debug)
102                 case []Type:
103                         var buf bytes.Buffer
104                         buf.WriteByte('[')
105                         for i, x := range a {
106                                 if i > 0 {
107                                         buf.WriteString(", ")
108                                 }
109                                 buf.WriteString(typeString(x, qf, debug))
110                         }
111                         buf.WriteByte(']')
112                         arg = buf.String()
113                 case []*TypeParam:
114                         var buf bytes.Buffer
115                         buf.WriteByte('[')
116                         for i, x := range a {
117                                 if i > 0 {
118                                         buf.WriteString(", ")
119                                 }
120                                 buf.WriteString(typeString(x, qf, debug)) // use typeString so we get subscripts when debugging
121                         }
122                         buf.WriteByte(']')
123                         arg = buf.String()
124                 }
125                 args[i] = arg
126         }
127         return fmt.Sprintf(format, args...)
128 }
129
130 func (check *Checker) trace(pos token.Pos, format string, args ...any) {
131         fmt.Printf("%s:\t%s%s\n",
132                 check.fset.Position(pos),
133                 strings.Repeat(".  ", check.indent),
134                 sprintf(check.fset, check.qualifier, true, format, args...),
135         )
136 }
137
138 // dump is only needed for debugging
139 func (check *Checker) dump(format string, args ...any) {
140         fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
141 }
142
143 func (check *Checker) err(err error) {
144         if err == nil {
145                 return
146         }
147         var e Error
148         isInternal := errors.As(err, &e)
149         // Cheap trick: Don't report errors with messages containing
150         // "invalid operand" or "invalid type" as those tend to be
151         // follow-on errors which don't add useful information. Only
152         // exclude them if these strings are not at the beginning,
153         // and only if we have at least one error already reported.
154         isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0)
155         if check.firstErr != nil && isInvalidErr {
156                 return
157         }
158
159         if isInternal {
160                 e.Msg = stripAnnotations(e.Msg)
161                 if check.errpos != nil {
162                         // If we have an internal error and the errpos override is set, use it to
163                         // augment our error positioning.
164                         // TODO(rFindley) we may also want to augment the error message and refer
165                         // to the position (pos) in the original expression.
166                         span := spanOf(check.errpos)
167                         e.Pos = span.pos
168                         e.go116start = span.start
169                         e.go116end = span.end
170                 }
171                 err = e
172         }
173
174         if check.firstErr == nil {
175                 check.firstErr = err
176         }
177
178         if trace {
179                 pos := e.Pos
180                 msg := e.Msg
181                 if !isInternal {
182                         msg = err.Error()
183                         pos = token.NoPos
184                 }
185                 check.trace(pos, "ERROR: %s", msg)
186         }
187
188         f := check.conf.Error
189         if f == nil {
190                 panic(bailout{}) // report only first error
191         }
192         f(err)
193 }
194
195 func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
196         span := spanOf(at)
197         return Error{
198                 Fset:       check.fset,
199                 Pos:        span.pos,
200                 Msg:        msg,
201                 Soft:       soft,
202                 go116code:  code,
203                 go116start: span.start,
204                 go116end:   span.end,
205         }
206 }
207
208 // newErrorf creates a new Error, but does not handle it.
209 func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...any) error {
210         msg := check.sprintf(format, args...)
211         return check.newError(at, code, soft, msg)
212 }
213
214 func (check *Checker) error(at positioner, code errorCode, msg string) {
215         check.err(check.newError(at, code, false, msg))
216 }
217
218 func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
219         check.error(at, code, check.sprintf(format, args...))
220 }
221
222 func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
223         check.err(check.newErrorf(at, code, true, format, args...))
224 }
225
226 func (check *Checker) invalidAST(at positioner, format string, args ...any) {
227         check.errorf(at, 0, "invalid AST: "+format, args...)
228 }
229
230 func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) {
231         check.errorf(at, code, "invalid argument: "+format, args...)
232 }
233
234 func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) {
235         check.errorf(at, code, "invalid operation: "+format, args...)
236 }
237
238 // The positioner interface is used to extract the position of type-checker
239 // errors.
240 type positioner interface {
241         Pos() token.Pos
242 }
243
244 // posSpan holds a position range along with a highlighted position within that
245 // range. This is used for positioning errors, with pos by convention being the
246 // first position in the source where the error is known to exist, and start
247 // and end defining the full span of syntax being considered when the error was
248 // detected. Invariant: start <= pos < end || start == pos == end.
249 type posSpan struct {
250         start, pos, end token.Pos
251 }
252
253 func (e posSpan) Pos() token.Pos {
254         return e.pos
255 }
256
257 // inNode creates a posSpan for the given node.
258 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
259 // first byte after node within the source).
260 func inNode(node ast.Node, pos token.Pos) posSpan {
261         start, end := node.Pos(), node.End()
262         if debug {
263                 assert(start <= pos && pos < end)
264         }
265         return posSpan{start, pos, end}
266 }
267
268 // atPos wraps a token.Pos to implement the positioner interface.
269 type atPos token.Pos
270
271 func (s atPos) Pos() token.Pos {
272         return token.Pos(s)
273 }
274
275 // spanOf extracts an error span from the given positioner. By default this is
276 // the trivial span starting and ending at pos, but this span is expanded when
277 // the argument naturally corresponds to a span of source code.
278 func spanOf(at positioner) posSpan {
279         switch x := at.(type) {
280         case nil:
281                 panic("nil positioner")
282         case posSpan:
283                 return x
284         case ast.Node:
285                 pos := x.Pos()
286                 return posSpan{pos, pos, x.End()}
287         case *operand:
288                 if x.expr != nil {
289                         pos := x.Pos()
290                         return posSpan{pos, pos, x.expr.End()}
291                 }
292                 return posSpan{token.NoPos, token.NoPos, token.NoPos}
293         default:
294                 pos := at.Pos()
295                 return posSpan{pos, pos, pos}
296         }
297 }
298
299 // stripAnnotations removes internal (type) annotations from s.
300 func stripAnnotations(s string) string {
301         var b strings.Builder
302         for _, r := range s {
303                 // strip #'s and subscript digits
304                 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
305                         b.WriteRune(r)
306                 }
307         }
308         if b.Len() < len(s) {
309                 return b.String()
310         }
311         return s
312 }