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