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