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