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 error reporting.
6 7 package types
8 9 import (
10 "fmt"
11 "go/ast"
12 "go/token"
13 . "internal/types/errors"
14 "runtime"
15 "strings"
16 )
17 18 func assert(p bool) {
19 if !p {
20 msg := "assertion failed"
21 // Include information about the assertion location. Due to panic recovery,
22 // this location is otherwise buried in the middle of the panicking stack.
23 if _, file, line, ok := runtime.Caller(1); ok {
24 msg = fmt.Sprintf("%s:%d: %s", file, line, msg)
25 }
26 panic(msg)
27 }
28 }
29 30 // An errorDesc describes part of a type-checking error.
31 type errorDesc struct {
32 posn positioner
33 msg string
34 }
35 36 // An error_ represents a type-checking error.
37 // A new error_ is created with Checker.newError.
38 // To report an error_, call error_.report.
39 type error_ struct {
40 check *Checker
41 desc []errorDesc
42 code Code
43 soft bool // TODO(gri) eventually determine this from an error code
44 }
45 46 // newError returns a new error_ with the given error code.
47 func (check *Checker) newError(code Code) *error_ {
48 if code == 0 {
49 panic("error code must not be 0")
50 }
51 return &error_{check: check, code: code}
52 }
53 54 // addf adds formatted error information to err.
55 // It may be called multiple times to provide additional information.
56 // The position of the first call to addf determines the position of the reported Error.
57 // Subsequent calls to addf provide additional information in the form of additional lines
58 // in the error message (types2) or continuation errors identified by a tab-indented error
59 // message (go/types).
60 func (err *error_) addf(at positioner, format string, args ...interface{}) {
61 err.desc = append(err.desc, errorDesc{at, err.check.sprintf(format, args...)})
62 }
63 64 // addAltDecl is a specialized form of addf reporting another declaration of obj.
65 func (err *error_) addAltDecl(obj Object) {
66 if pos := obj.Pos(); pos.IsValid() {
67 // We use "other" rather than "previous" here because
68 // the first declaration seen may not be textually
69 // earlier in the source.
70 err.addf(obj, "other declaration of %s", obj.Name())
71 }
72 }
73 74 func (err *error_) empty() bool {
75 return err.desc == nil
76 }
77 78 func (err *error_) posn() positioner {
79 if err.empty() {
80 return noposn
81 }
82 return err.desc[0].posn
83 }
84 85 // msg returns the formatted error message without the primary error position pos().
86 func (err *error_) msg() string {
87 if err.empty() {
88 return "no error"
89 }
90 91 var buf strings.Builder
92 for i := range err.desc {
93 p := &err.desc[i]
94 if i > 0 {
95 fmt.Fprint(&buf, "\n\t")
96 if p.posn.Pos().IsValid() {
97 fmt.Fprintf(&buf, "%s: ", err.check.fset.Position(p.posn.Pos()))
98 }
99 }
100 buf.WriteString(p.msg)
101 }
102 return buf.String()
103 }
104 105 // report reports the error err, setting check.firstError if necessary.
106 func (err *error_) report() {
107 if err.empty() {
108 panic("no error")
109 }
110 111 // Cheap trick: Don't report errors with messages containing
112 // "invalid operand" or "invalid type" as those tend to be
113 // follow-on errors which don't add useful information. Only
114 // exclude them if these strings are not at the beginning,
115 // and only if we have at least one error already reported.
116 check := err.check
117 if check.firstErr != nil {
118 // It is sufficient to look at the first sub-error only.
119 msg := err.desc[0].msg
120 if strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0 {
121 return
122 }
123 }
124 125 if check.conf._Trace {
126 check.trace(err.posn().Pos(), "ERROR: %s (code = %d)", err.desc[0].msg, err.code)
127 }
128 129 // In go/types, if there is a sub-error with a valid position,
130 // call the typechecker error handler for each sub-error.
131 // Otherwise, call it once, with a single combined message.
132 multiError := false
133 if !isTypes2 {
134 for i := 1; i < len(err.desc); i++ {
135 if err.desc[i].posn.Pos().IsValid() {
136 multiError = true
137 break
138 }
139 }
140 }
141 142 if multiError {
143 for i := range err.desc {
144 p := &err.desc[i]
145 check.handleError(i, p.posn, err.code, p.msg, err.soft)
146 }
147 } else {
148 check.handleError(0, err.posn(), err.code, err.msg(), err.soft)
149 }
150 151 // make sure the error is not reported twice
152 err.desc = nil
153 }
154 155 // handleError should only be called by error_.report.
156 func (check *Checker) handleError(index int, posn positioner, code Code, msg string, soft bool) {
157 assert(code != 0)
158 159 if index == 0 {
160 // If we are encountering an error while evaluating an inherited
161 // constant initialization expression, pos is the position of
162 // the original expression, and not of the currently declared
163 // constant identifier. Use the provided errpos instead.
164 // TODO(gri) We may also want to augment the error message and
165 // refer to the position (pos) in the original expression.
166 if check.errpos != nil && check.errpos.Pos().IsValid() {
167 assert(check.iota != nil)
168 posn = check.errpos
169 }
170 171 // Report invalid syntax trees explicitly.
172 if code == InvalidSyntaxTree {
173 msg = "invalid syntax tree: " + msg
174 }
175 176 // If we have a URL for error codes, add a link to the first line.
177 if check.conf._ErrorURL != "" {
178 url := fmt.Sprintf(check.conf._ErrorURL, code)
179 if i := strings.Index(msg, "\n"); i >= 0 {
180 msg = msg[:i] + url + msg[i:]
181 } else {
182 msg += url
183 }
184 }
185 } else {
186 // Indent sub-error.
187 // Position information is passed explicitly to Error, below.
188 msg = "\t" + msg
189 }
190 191 span := spanOf(posn)
192 e := Error{
193 Fset: check.fset,
194 Pos: span.pos,
195 Msg: stripAnnotations(msg),
196 Soft: soft,
197 go116code: code,
198 go116start: span.start,
199 go116end: span.end,
200 }
201 202 if check.errpos != nil {
203 // If we have an internal error and the errpos override is set, use it to
204 // augment our error positioning.
205 // TODO(rFindley) we may also want to augment the error message and refer
206 // to the position (pos) in the original expression.
207 span := spanOf(check.errpos)
208 e.Pos = span.pos
209 e.go116start = span.start
210 e.go116end = span.end
211 }
212 213 if check.firstErr == nil {
214 check.firstErr = e
215 }
216 217 f := check.conf.Error
218 if f == nil {
219 panic(bailout{}) // record first error and exit
220 }
221 f(e)
222 }
223 224 const (
225 invalidArg = "invalid argument: "
226 invalidOp = "invalid operation: "
227 )
228 229 // The positioner interface is used to extract the position of type-checker errors.
230 type positioner interface {
231 Pos() token.Pos
232 }
233 234 func (check *Checker) error(at positioner, code Code, msg string) {
235 err := check.newError(code)
236 err.addf(at, "%s", msg)
237 err.report()
238 }
239 240 func (check *Checker) errorf(at positioner, code Code, format string, args ...any) {
241 err := check.newError(code)
242 err.addf(at, format, args...)
243 err.report()
244 }
245 246 func (check *Checker) softErrorf(at positioner, code Code, format string, args ...any) {
247 err := check.newError(code)
248 err.addf(at, format, args...)
249 err.soft = true
250 err.report()
251 }
252 253 func (check *Checker) versionErrorf(at positioner, v goVersion, format string, args ...any) {
254 msg := check.sprintf(format, args...)
255 err := check.newError(UnsupportedFeature)
256 err.addf(at, "%s requires %s or later", msg, v)
257 err.report()
258 }
259 260 // atPos wraps a token.Pos to implement the positioner interface.
261 type atPos token.Pos
262 263 func (s atPos) Pos() token.Pos {
264 return token.Pos(s)
265 }
266 267 // posSpan holds a position range along with a highlighted position within that
268 // range. This is used for positioning errors, with pos by convention being the
269 // first position in the source where the error is known to exist, and start
270 // and end defining the full span of syntax being considered when the error was
271 // detected. Invariant: start <= pos < end || start == pos == end.
272 type posSpan struct {
273 start, pos, end token.Pos
274 }
275 276 func (e posSpan) Pos() token.Pos {
277 return e.pos
278 }
279 280 // inNode creates a posSpan for the given node.
281 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
282 // first byte after node within the source).
283 func inNode(node ast.Node, pos token.Pos) posSpan {
284 start, end := node.Pos(), node.End()
285 if debug {
286 assert(start <= pos && pos < end)
287 }
288 return posSpan{start, pos, end}
289 }
290 291 // spanOf extracts an error span from the given positioner. By default this is
292 // the trivial span starting and ending at pos, but this span is expanded when
293 // the argument naturally corresponds to a span of source code.
294 func spanOf(at positioner) posSpan {
295 switch x := at.(type) {
296 case nil:
297 panic("nil positioner")
298 case posSpan:
299 return x
300 case ast.Node:
301 pos := x.Pos()
302 return posSpan{pos, pos, x.End()}
303 case *operand:
304 if x.expr != nil {
305 pos := x.Pos()
306 return posSpan{pos, pos, x.expr.End()}
307 }
308 return posSpan{nopos, nopos, nopos}
309 default:
310 pos := at.Pos()
311 return posSpan{pos, pos, pos}
312 }
313 }
314