Source file
src/go/types/errors.go
1
2
3
4
5
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
24
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
37
38 type error_ struct {
39 desc []errorDesc
40 code Code
41 soft bool
42 }
43
44
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
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
89
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
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
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
112
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
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
212 func (check *Checker) dump(format string, args ...any) {
213 fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
214 }
215
216
217
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
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
253
254
255
256
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
265
266
267
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{})
288 }
289 f(err)
290 }
291
292 const (
293 invalidArg = "invalid argument: "
294 invalidOp = "invalid operation: "
295 )
296
297
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 goVersion, 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
327
328 type positioner interface {
329 Pos() token.Pos
330 }
331
332
333
334
335
336
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
346
347
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
357 type atPos token.Pos
358
359 func (s atPos) Pos() token.Pos {
360 return token.Pos(s)
361 }
362
363
364
365
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
388 func stripAnnotations(s string) string {
389 var buf strings.Builder
390 for _, r := range s {
391
392 if r < '₀' || '₀'+10 <= r {
393 buf.WriteRune(r)
394 }
395 }
396 if buf.Len() < len(s) {
397 return buf.String()
398 }
399 return s
400 }
401
View as plain text