]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/errors.go
cmd/compile/internal/types2: record error code in Error struct
[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         switch code {
225         case InvalidSyntaxTree:
226                 msg = "invalid syntax tree: " + msg
227         case 0:
228                 panic("no error code provided")
229         }
230
231         // Cheap trick: Don't report errors with messages containing
232         // "invalid operand" or "invalid type" as those tend to be
233         // follow-on errors which don't add useful information. Only
234         // exclude them if these strings are not at the beginning,
235         // and only if we have at least one error already reported.
236         if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
237                 return
238         }
239
240         pos := posFor(at)
241
242         // If we are encountering an error while evaluating an inherited
243         // constant initialization expression, pos is the position of in
244         // the original expression, and not of the currently declared
245         // constant identifier. Use the provided errpos instead.
246         // TODO(gri) We may also want to augment the error message and
247         // refer to the position (pos) in the original expression.
248         if check.errpos.IsKnown() {
249                 assert(check.iota != nil)
250                 pos = check.errpos
251         }
252
253         err := Error{pos, stripAnnotations(msg), msg, soft, code}
254         if check.firstErr == nil {
255                 check.firstErr = err
256         }
257
258         if check.conf.Trace {
259                 check.trace(pos, "ERROR: %s", msg)
260         }
261
262         f := check.conf.Error
263         if f == nil {
264                 panic(bailout{}) // report only first error
265         }
266         f(err)
267 }
268
269 const (
270         invalidArg = "invalid argument: "
271         invalidOp  = "invalid operation: "
272 )
273
274 type poser interface {
275         Pos() syntax.Pos
276 }
277
278 func (check *Checker) error(at poser, code Code, msg string) {
279         check.err(at, code, msg, false)
280 }
281
282 func (check *Checker) errorf(at poser, code Code, format string, args ...interface{}) {
283         check.err(at, code, check.sprintf(format, args...), false)
284 }
285
286 func (check *Checker) softErrorf(at poser, code Code, format string, args ...interface{}) {
287         check.err(at, code, check.sprintf(format, args...), true)
288 }
289
290 func (check *Checker) versionErrorf(at poser, goVersion string, format string, args ...interface{}) {
291         msg := check.sprintf(format, args...)
292         msg = fmt.Sprintf("%s requires %s or later", msg, goVersion)
293         check.err(at, UnsupportedFeature, msg, true)
294 }
295
296 // posFor reports the left (= start) position of at.
297 func posFor(at poser) syntax.Pos {
298         switch x := at.(type) {
299         case *operand:
300                 if x.expr != nil {
301                         return syntax.StartPos(x.expr)
302                 }
303         case syntax.Node:
304                 return syntax.StartPos(x)
305         }
306         return at.Pos()
307 }
308
309 // stripAnnotations removes internal (type) annotations from s.
310 func stripAnnotations(s string) string {
311         var buf strings.Builder
312         for _, r := range s {
313                 // strip #'s and subscript digits
314                 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
315                         buf.WriteRune(r)
316                 }
317         }
318         if buf.Len() < len(s) {
319                 return buf.String()
320         }
321         return s
322 }