github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/typecheck/error.go (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 package typecheck 6 7 import ( 8 "errors" 9 "fmt" 10 "runtime" 11 ) 12 13 // TestCalldepth may be overridden by a user to add call depths to 14 // errors that are constructed by NewError. This is useful for 15 // testing that error messages capture the correct locations. 16 var TestCalldepth = 0 17 18 // Error represents a typechecking error. It wraps an underlying 19 // error with a location, as captured by NewError. 20 type Error struct { 21 Err error 22 File string 23 Line int 24 } 25 26 // NewError creates a new typechecking error at the given calldepth. 27 // The returned Error wraps err with the caller's location. 28 func NewError(calldepth int, err error) *Error { 29 e := &Error{Err: err} 30 var ok bool 31 _, e.File, e.Line, ok = runtime.Caller(calldepth + 1 + TestCalldepth) 32 if !ok { 33 e.File = "<unknown>" 34 } 35 return e 36 } 37 38 // Errorf constructs an error in the manner of fmt.Errorf. 39 func Errorf(calldepth int, format string, args ...interface{}) *Error { 40 return NewError(calldepth+1, fmt.Errorf(format, args...)) 41 } 42 43 // Panic constructs a typechecking error and then panics with it. 44 func Panic(calldepth int, message string) { 45 panic(NewError(calldepth+1, errors.New(message))) 46 } 47 48 // Panicf constructs a new formatted typechecking error and then 49 // panics with it. 50 func Panicf(calldepth int, format string, args ...interface{}) { 51 panic(Errorf(calldepth+1, format, args...)) 52 } 53 54 // Error implements error. 55 func (err *Error) Error() string { 56 return fmt.Sprintf("%s:%d: %v", err.File, err.Line, err.Err) 57 } 58 59 // Location rewrites typecheck errors to use the provided location 60 // instead of the one computed by Panic. This allows a caller to 61 // attribute typechecking errors where appropriate. Location should 62 // only be used as a defer function, as it recovers (and rewrites) 63 // panics. 64 // 65 // file, line := ... 66 // defer Location(file, line) 67 func Location(file string, line int) { 68 e := recover() 69 if e == nil { 70 return 71 } 72 err, ok := e.(*Error) 73 if !ok { 74 panic(e) 75 } 76 err.File = file 77 err.Line = line 78 panic(err) 79 }