github.com/bir3/gocompiler@v0.3.205/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 "github.com/bir3/gocompiler/src/go/ast" 13 "github.com/bir3/gocompiler/src/go/token" 14 . "github.com/bir3/gocompiler/src/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 token.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 span := spanOf(errp.desc[0].posn) 232 e := Error{ 233 Fset: check.fset, 234 Pos: span.pos, 235 Msg: msg, 236 Soft: errp.soft, 237 go116code: errp.code, 238 go116start: span.start, 239 go116end: span.end, 240 } 241 242 // Cheap trick: Don't report errors with messages containing 243 // "invalid operand" or "invalid type" as those tend to be 244 // follow-on errors which don't add useful information. Only 245 // exclude them if these strings are not at the beginning, 246 // and only if we have at least one error already reported. 247 isInvalidErr := strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0 248 if check.firstErr != nil && isInvalidErr { 249 return 250 } 251 252 e.Msg = stripAnnotations(e.Msg) 253 if check.errpos != nil { 254 // If we have an internal error and the errpos override is set, use it to 255 // augment our error positioning. 256 // TODO(rFindley) we may also want to augment the error message and refer 257 // to the position (pos) in the original expression. 258 span := spanOf(check.errpos) 259 e.Pos = span.pos 260 e.go116start = span.start 261 e.go116end = span.end 262 } 263 err := e 264 265 if check.firstErr == nil { 266 check.firstErr = err 267 } 268 269 if trace { 270 pos := e.Pos 271 msg := e.Msg 272 check.trace(pos, "ERROR: %s", msg) 273 } 274 275 f := check.conf.Error 276 if f == nil { 277 panic(bailout{}) // report only first error 278 } 279 f(err) 280 } 281 282 const ( 283 invalidArg = "invalid argument: " 284 invalidOp = "invalid operation: " 285 ) 286 287 // newErrorf creates a new error_ for later reporting with check.report. 288 func newErrorf(at positioner, code Code, format string, args ...any) *error_ { 289 return &error_{ 290 desc: []errorDesc{{at, format, args}}, 291 code: code, 292 } 293 } 294 295 func (check *Checker) error(at positioner, code Code, msg string) { 296 check.report(newErrorf(at, code, msg)) 297 } 298 299 func (check *Checker) errorf(at positioner, code Code, format string, args ...any) { 300 check.report(newErrorf(at, code, format, args...)) 301 } 302 303 func (check *Checker) softErrorf(at positioner, code Code, format string, args ...any) { 304 err := newErrorf(at, code, format, args...) 305 err.soft = true 306 check.report(err) 307 } 308 309 func (check *Checker) versionErrorf(at positioner, goVersion string, format string, args ...interface{}) { 310 msg := check.sprintf(format, args...) 311 var err *error_ 312 err = newErrorf(at, UnsupportedFeature, "%s requires %s or later", msg, goVersion) 313 check.report(err) 314 } 315 316 // The positioner interface is used to extract the position of type-checker 317 // errors. 318 type positioner interface { 319 Pos() token.Pos 320 } 321 322 // posSpan holds a position range along with a highlighted position within that 323 // range. This is used for positioning errors, with pos by convention being the 324 // first position in the source where the error is known to exist, and start 325 // and end defining the full span of syntax being considered when the error was 326 // detected. Invariant: start <= pos < end || start == pos == end. 327 type posSpan struct { 328 start, pos, end token.Pos 329 } 330 331 func (e posSpan) Pos() token.Pos { 332 return e.pos 333 } 334 335 // inNode creates a posSpan for the given node. 336 // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the 337 // first byte after node within the source). 338 func inNode(node ast.Node, pos token.Pos) posSpan { 339 start, end := node.Pos(), node.End() 340 if debug { 341 assert(start <= pos && pos < end) 342 } 343 return posSpan{start, pos, end} 344 } 345 346 // atPos wraps a token.Pos to implement the positioner interface. 347 type atPos token.Pos 348 349 func (s atPos) Pos() token.Pos { 350 return token.Pos(s) 351 } 352 353 // spanOf extracts an error span from the given positioner. By default this is 354 // the trivial span starting and ending at pos, but this span is expanded when 355 // the argument naturally corresponds to a span of source code. 356 func spanOf(at positioner) posSpan { 357 switch x := at.(type) { 358 case nil: 359 panic("nil positioner") 360 case posSpan: 361 return x 362 case ast.Node: 363 pos := x.Pos() 364 return posSpan{pos, pos, x.End()} 365 case *operand: 366 if x.expr != nil { 367 pos := x.Pos() 368 return posSpan{pos, pos, x.expr.End()} 369 } 370 return posSpan{token.NoPos, token.NoPos, token.NoPos} 371 default: 372 pos := at.Pos() 373 return posSpan{pos, pos, pos} 374 } 375 } 376 377 // stripAnnotations removes internal (type) annotations from s. 378 func stripAnnotations(s string) string { 379 var buf strings.Builder 380 for _, r := range s { 381 // strip #'s and subscript digits 382 if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 383 buf.WriteRune(r) 384 } 385 } 386 if buf.Len() < len(s) { 387 return buf.String() 388 } 389 return s 390 }