]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/errors.go
go/types, types2: use zero error code to indicate unset error code
[gostls13.git] / src / cmd / compile / internal / types2 / 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 types2
8
9 import (
10         "bytes"
11         "cmd/compile/internal/syntax"
12         "fmt"
13         . "internal/types/errors"
14         "runtime"
15         "strconv"
16         "strings"
17 )
18
19 func assert(p bool) {
20         if !p {
21                 msg := "assertion failed"
22                 // Include information about the assertion location. Due to panic recovery,
23                 // this location is otherwise buried in the middle of the panicking stack.
24                 if _, file, line, ok := runtime.Caller(1); ok {
25                         msg = fmt.Sprintf("%s:%d: %s", file, line, msg)
26                 }
27                 panic(msg)
28         }
29 }
30
31 func unreachable() {
32         panic("unreachable")
33 }
34
35 // An error_ represents a type-checking error.
36 // To report an error_, call Checker.report.
37 type error_ struct {
38         desc []errorDesc
39         code Code
40         soft bool // TODO(gri) eventually determine this from an error code
41 }
42
43 // An errorDesc describes part of a type-checking error.
44 type errorDesc struct {
45         pos    syntax.Pos
46         format string
47         args   []interface{}
48 }
49
50 func (err *error_) empty() bool {
51         return err.desc == nil
52 }
53
54 func (err *error_) pos() syntax.Pos {
55         if err.empty() {
56                 return nopos
57         }
58         return err.desc[0].pos
59 }
60
61 func (err *error_) msg(qf Qualifier) string {
62         if err.empty() {
63                 return "no error"
64         }
65         var buf strings.Builder
66         for i := range err.desc {
67                 p := &err.desc[i]
68                 if i > 0 {
69                         fmt.Fprint(&buf, "\n\t")
70                         if p.pos.IsKnown() {
71                                 fmt.Fprintf(&buf, "%s: ", p.pos)
72                         }
73                 }
74                 buf.WriteString(sprintf(qf, false, p.format, p.args...))
75         }
76         return buf.String()
77 }
78
79 // String is for testing.
80 func (err *error_) String() string {
81         if err.empty() {
82                 return "no error"
83         }
84         return fmt.Sprintf("%s: %s", err.pos(), err.msg(nil))
85 }
86
87 // errorf adds formatted error information to err.
88 // It may be called multiple times to provide additional information.
89 func (err *error_) errorf(at poser, format string, args ...interface{}) {
90         err.desc = append(err.desc, errorDesc{posFor(at), format, args})
91 }
92
93 func sprintf(qf Qualifier, tpSubscripts bool, format string, args ...interface{}) string {
94         for i, arg := range args {
95                 switch a := arg.(type) {
96                 case nil:
97                         arg = "<nil>"
98                 case operand:
99                         panic("got operand instead of *operand")
100                 case *operand:
101                         arg = operandString(a, qf)
102                 case syntax.Pos:
103                         arg = a.String()
104                 case syntax.Expr:
105                         arg = syntax.String(a)
106                 case []syntax.Expr:
107                         var buf strings.Builder
108                         buf.WriteByte('[')
109                         for i, x := range a {
110                                 if i > 0 {
111                                         buf.WriteString(", ")
112                                 }
113                                 buf.WriteString(syntax.String(x))
114                         }
115                         buf.WriteByte(']')
116                         arg = buf.String()
117                 case Object:
118                         arg = ObjectString(a, qf)
119                 case Type:
120                         var buf bytes.Buffer
121                         w := newTypeWriter(&buf, qf)
122                         w.tpSubscripts = tpSubscripts
123                         w.typ(a)
124                         arg = buf.String()
125                 case []Type:
126                         var buf bytes.Buffer
127                         w := newTypeWriter(&buf, qf)
128                         w.tpSubscripts = tpSubscripts
129                         buf.WriteByte('[')
130                         for i, x := range a {
131                                 if i > 0 {
132                                         buf.WriteString(", ")
133                                 }
134                                 w.typ(x)
135                         }
136                         buf.WriteByte(']')
137                         arg = buf.String()
138                 case []*TypeParam:
139                         var buf bytes.Buffer
140                         w := newTypeWriter(&buf, qf)
141                         w.tpSubscripts = tpSubscripts
142                         buf.WriteByte('[')
143                         for i, x := range a {
144                                 if i > 0 {
145                                         buf.WriteString(", ")
146                                 }
147                                 w.typ(x)
148                         }
149                         buf.WriteByte(']')
150                         arg = buf.String()
151                 }
152                 args[i] = arg
153         }
154         return fmt.Sprintf(format, args...)
155 }
156
157 func (check *Checker) qualifier(pkg *Package) string {
158         // Qualify the package unless it's the package being type-checked.
159         if pkg != check.pkg {
160                 if check.pkgPathMap == nil {
161                         check.pkgPathMap = make(map[string]map[string]bool)
162                         check.seenPkgMap = make(map[*Package]bool)
163                         check.markImports(check.pkg)
164                 }
165                 // If the same package name was used by multiple packages, display the full path.
166                 if len(check.pkgPathMap[pkg.name]) > 1 {
167                         return strconv.Quote(pkg.path)
168                 }
169                 return pkg.name
170         }
171         return ""
172 }
173
174 // markImports recursively walks pkg and its imports, to record unique import
175 // paths in pkgPathMap.
176 func (check *Checker) markImports(pkg *Package) {
177         if check.seenPkgMap[pkg] {
178                 return
179         }
180         check.seenPkgMap[pkg] = true
181
182         forName, ok := check.pkgPathMap[pkg.name]
183         if !ok {
184                 forName = make(map[string]bool)
185                 check.pkgPathMap[pkg.name] = forName
186         }
187         forName[pkg.path] = true
188
189         for _, imp := range pkg.imports {
190                 check.markImports(imp)
191         }
192 }
193
194 // check may be nil.
195 func (check *Checker) sprintf(format string, args ...interface{}) string {
196         var qf Qualifier
197         if check != nil {
198                 qf = check.qualifier
199         }
200         return sprintf(qf, false, format, args...)
201 }
202
203 func (check *Checker) report(err *error_) {
204         if err.empty() {
205                 panic("no error to report")
206         }
207         check.err(err.pos(), err.code, err.msg(check.qualifier), err.soft)
208 }
209
210 func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) {
211         fmt.Printf("%s:\t%s%s\n",
212                 pos,
213                 strings.Repeat(".  ", check.indent),
214                 sprintf(check.qualifier, true, format, args...),
215         )
216 }
217
218 // dump is only needed for debugging
219 func (check *Checker) dump(format string, args ...interface{}) {
220         fmt.Println(sprintf(check.qualifier, true, format, args...))
221 }
222
223 func (check *Checker) err(at poser, code Code, msg string, soft bool) {
224         if code == 0 {
225                 panic("no error code provided")
226         }
227
228         // Cheap trick: Don't report errors with messages containing
229         // "invalid operand" or "invalid type" as those tend to be
230         // follow-on errors which don't add useful information. Only
231         // exclude them if these strings are not at the beginning,
232         // and only if we have at least one error already reported.
233         if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
234                 return
235         }
236
237         pos := posFor(at)
238
239         // If we are encountering an error while evaluating an inherited
240         // constant initialization expression, pos is the position of in
241         // the original expression, and not of the currently declared
242         // constant identifier. Use the provided errpos instead.
243         // TODO(gri) We may also want to augment the error message and
244         // refer to the position (pos) in the original expression.
245         if check.errpos.IsKnown() {
246                 assert(check.iota != nil)
247                 pos = check.errpos
248         }
249
250         err := Error{pos, stripAnnotations(msg), msg, soft}
251         if check.firstErr == nil {
252                 check.firstErr = err
253         }
254
255         if check.conf.Trace {
256                 check.trace(pos, "ERROR: %s", msg)
257         }
258
259         f := check.conf.Error
260         if f == nil {
261                 panic(bailout{}) // report only first error
262         }
263         f(err)
264 }
265
266 const (
267         invalidAST = "invalid AST: "
268         invalidArg = "invalid argument: "
269         invalidOp  = "invalid operation: "
270 )
271
272 type poser interface {
273         Pos() syntax.Pos
274 }
275
276 func (check *Checker) error(at poser, code Code, msg string) {
277         check.err(at, code, msg, false)
278 }
279
280 func (check *Checker) errorf(at poser, code Code, format string, args ...interface{}) {
281         check.err(at, code, check.sprintf(format, args...), false)
282 }
283
284 func (check *Checker) softErrorf(at poser, code Code, format string, args ...interface{}) {
285         check.err(at, code, check.sprintf(format, args...), true)
286 }
287
288 func (check *Checker) versionErrorf(at poser, goVersion string, format string, args ...interface{}) {
289         msg := check.sprintf(format, args...)
290         msg = fmt.Sprintf("%s requires %s or later", msg, goVersion)
291         check.err(at, UnsupportedFeature, msg, true)
292 }
293
294 // posFor reports the left (= start) position of at.
295 func posFor(at poser) syntax.Pos {
296         switch x := at.(type) {
297         case *operand:
298                 if x.expr != nil {
299                         return syntax.StartPos(x.expr)
300                 }
301         case syntax.Node:
302                 return syntax.StartPos(x)
303         }
304         return at.Pos()
305 }
306
307 // stripAnnotations removes internal (type) annotations from s.
308 func stripAnnotations(s string) string {
309         var buf strings.Builder
310         for _, r := range s {
311                 // strip #'s and subscript digits
312                 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
313                         buf.WriteRune(r)
314                 }
315         }
316         if buf.Len() < len(s) {
317                 return buf.String()
318         }
319         return s
320 }