github.com/AndrienkoAleksandr/go@v0.0.19/src/go/types/errors.go (about) 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 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 // Include information about the assertion location. Due to panic recovery, 24 // this location is otherwise buried in the middle of the panicking stack. 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 // An error_ represents a type-checking error. 37 // To report an error_, call Checker.report. 38 type error_ struct { 39 desc []errorDesc 40 code Code 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 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 // String is for testing. 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 // errorf adds formatted error information to err. 89 // It may be called multiple times to provide additional information. 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 // Qualify the package unless it's the package being type-checked. 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 // If the same package name was used by multiple packages, display the full path. 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 // markImports recursively walks pkg and its imports, to record unique import 112 // paths in pkgPathMap. 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 // check may be nil. 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 // dump is only needed for debugging 212 func (check *Checker) dump(format string, args ...any) { 213 fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...)) 214 } 215 216 // Report records the error pointed to by errp, setting check.firstError if 217 // necessary. 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 // If we have an URL for error codes, add a link to the first line. 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 // Cheap trick: Don't report errors with messages containing 253 // "invalid operand" or "invalid type" as those tend to be 254 // follow-on errors which don't add useful information. Only 255 // exclude them if these strings are not at the beginning, 256 // and only if we have at least one error already reported. 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 // If we have an internal error and the errpos override is set, use it to 265 // augment our error positioning. 266 // TODO(rFindley) we may also want to augment the error message and refer 267 // to the position (pos) in the original expression. 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{}) // report only first error 288 } 289 f(err) 290 } 291 292 const ( 293 invalidArg = "invalid argument: " 294 invalidOp = "invalid operation: " 295 ) 296 297 // newErrorf creates a new error_ for later reporting with check.report. 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 version, 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 // The positioner interface is used to extract the position of type-checker 327 // errors. 328 type positioner interface { 329 Pos() token.Pos 330 } 331 332 // posSpan holds a position range along with a highlighted position within that 333 // range. This is used for positioning errors, with pos by convention being the 334 // first position in the source where the error is known to exist, and start 335 // and end defining the full span of syntax being considered when the error was 336 // detected. Invariant: start <= pos < end || start == pos == end. 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 // inNode creates a posSpan for the given node. 346 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the 347 // first byte after node within the source). 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 // atPos wraps a token.Pos to implement the positioner interface. 357 type atPos token.Pos 358 359 func (s atPos) Pos() token.Pos { 360 return token.Pos(s) 361 } 362 363 // spanOf extracts an error span from the given positioner. By default this is 364 // the trivial span starting and ending at pos, but this span is expanded when 365 // the argument naturally corresponds to a span of source code. 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 // stripAnnotations removes internal (type) annotations from s. 388 func stripAnnotations(s string) string { 389 var buf strings.Builder 390 for _, r := range s { 391 // strip #'s and subscript digits 392 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 393 buf.WriteRune(r) 394 } 395 } 396 if buf.Len() < len(s) { 397 return buf.String() 398 } 399 return s 400 }