package types
import (
"bytes"
"fmt"
"go/ast"
"go/token"
. "internal/types/errors"
"runtime"
"strconv"
"strings"
)
func assert(p bool ) {
if !p {
msg := "assertion failed"
if _ , file , line , ok := runtime .Caller (1 ); ok {
msg = fmt .Sprintf ("%s:%d: %s" , file , line , msg )
}
panic (msg )
}
}
func unreachable() {
panic ("unreachable" )
}
type error_ struct {
desc []errorDesc
code Code
soft bool
}
type errorDesc struct {
posn positioner
format string
args []interface {}
}
func (err *error_ ) empty () bool {
return err .desc == nil
}
func (err *error_ ) pos () token .Pos {
if err .empty () {
return nopos
}
return err .desc [0 ].posn .Pos ()
}
func (err *error_ ) msg (fset *token .FileSet , qf Qualifier ) string {
if err .empty () {
return "no error"
}
var buf strings .Builder
for i := range err .desc {
p := &err .desc [i ]
if i > 0 {
fmt .Fprint (&buf , "\n\t" )
if p .posn .Pos ().IsValid () {
fmt .Fprintf (&buf , "%s: " , fset .Position (p .posn .Pos ()))
}
}
buf .WriteString (sprintf (fset , qf , false , p .format , p .args ...))
}
return buf .String ()
}
func (err *error_ ) String () string {
if err .empty () {
return "no error"
}
return fmt .Sprintf ("%d: %s" , err .pos (), err .msg (nil , nil ))
}
func (err *error_ ) errorf (at token .Pos , format string , args ...interface {}) {
err .desc = append (err .desc , errorDesc {atPos (at ), format , args })
}
func (check *Checker ) qualifier (pkg *Package ) string {
if pkg != check .pkg {
if check .pkgPathMap == nil {
check .pkgPathMap = make (map [string ]map [string ]bool )
check .seenPkgMap = make (map [*Package ]bool )
check .markImports (check .pkg )
}
if len (check .pkgPathMap [pkg .name ]) > 1 {
return strconv .Quote (pkg .path )
}
return pkg .name
}
return ""
}
func (check *Checker ) markImports (pkg *Package ) {
if check .seenPkgMap [pkg ] {
return
}
check .seenPkgMap [pkg ] = true
forName , ok := check .pkgPathMap [pkg .name ]
if !ok {
forName = make (map [string ]bool )
check .pkgPathMap [pkg .name ] = forName
}
forName [pkg .path ] = true
for _ , imp := range pkg .imports {
check .markImports (imp )
}
}
func (check *Checker ) sprintf (format string , args ...any ) string {
var fset *token .FileSet
var qf Qualifier
if check != nil {
fset = check .fset
qf = check .qualifier
}
return sprintf (fset , qf , false , format , args ...)
}
func sprintf(fset *token .FileSet , qf Qualifier , tpSubscripts bool , format string , args ...any ) string {
for i , arg := range args {
switch a := arg .(type ) {
case nil :
arg = "<nil>"
case operand :
panic ("got operand instead of *operand" )
case *operand :
arg = operandString (a , qf )
case token .Pos :
if fset != nil {
arg = fset .Position (a ).String ()
}
case ast .Expr :
arg = ExprString (a )
case []ast .Expr :
var buf bytes .Buffer
buf .WriteByte ('[' )
writeExprList (&buf , a )
buf .WriteByte (']' )
arg = buf .String ()
case Object :
arg = ObjectString (a , qf )
case Type :
var buf bytes .Buffer
w := newTypeWriter (&buf , qf )
w .tpSubscripts = tpSubscripts
w .typ (a )
arg = buf .String ()
case []Type :
var buf bytes .Buffer
w := newTypeWriter (&buf , qf )
w .tpSubscripts = tpSubscripts
buf .WriteByte ('[' )
for i , x := range a {
if i > 0 {
buf .WriteString (", " )
}
w .typ (x )
}
buf .WriteByte (']' )
arg = buf .String ()
case []*TypeParam :
var buf bytes .Buffer
w := newTypeWriter (&buf , qf )
w .tpSubscripts = tpSubscripts
buf .WriteByte ('[' )
for i , x := range a {
if i > 0 {
buf .WriteString (", " )
}
w .typ (x )
}
buf .WriteByte (']' )
arg = buf .String ()
}
args [i ] = arg
}
return fmt .Sprintf (format , args ...)
}
func (check *Checker ) trace (pos token .Pos , format string , args ...any ) {
fmt .Printf ("%s:\t%s%s\n" ,
check .fset .Position (pos ),
strings .Repeat (". " , check .indent ),
sprintf (check .fset , check .qualifier , true , format , args ...),
)
}
func (check *Checker ) dump (format string , args ...any ) {
fmt .Println (sprintf (check .fset , check .qualifier , true , format , args ...))
}
func (check *Checker ) report (errp *error_ ) {
if errp .empty () {
panic ("empty error details" )
}
msg := errp .msg (check .fset , check .qualifier )
switch errp .code {
case InvalidSyntaxTree :
msg = "invalid AST: " + msg
case 0 :
panic ("no error code provided" )
}
if errp .code != 0 && check .conf ._ErrorURL != "" {
u := fmt .Sprintf (check .conf ._ErrorURL , errp .code )
if i := strings .Index (msg , "\n" ); i >= 0 {
msg = msg [:i ] + u + msg [i :]
} else {
msg += u
}
}
span := spanOf (errp .desc [0 ].posn )
e := Error {
Fset : check .fset ,
Pos : span .pos ,
Msg : msg ,
Soft : errp .soft ,
go116code : errp .code ,
go116start : span .start ,
go116end : span .end ,
}
isInvalidErr := strings .Index (e .Msg , "invalid operand" ) > 0 || strings .Index (e .Msg , "invalid type" ) > 0
if check .firstErr != nil && isInvalidErr {
return
}
e .Msg = stripAnnotations (e .Msg )
if check .errpos != nil {
span := spanOf (check .errpos )
e .Pos = span .pos
e .go116start = span .start
e .go116end = span .end
}
err := e
if check .firstErr == nil {
check .firstErr = err
}
if check .conf ._Trace {
pos := e .Pos
msg := e .Msg
check .trace (pos , "ERROR: %s" , msg )
}
f := check .conf .Error
if f == nil {
panic (bailout {})
}
f (err )
}
const (
invalidArg = "invalid argument: "
invalidOp = "invalid operation: "
)
func newErrorf(at positioner , code Code , format string , args ...any ) *error_ {
return &error_ {
desc : []errorDesc {{at , format , args }},
code : code ,
}
}
func (check *Checker ) error (at positioner , code Code , msg string ) {
check .report (newErrorf (at , code , "%s" , msg ))
}
func (check *Checker ) errorf (at positioner , code Code , format string , args ...any ) {
check .report (newErrorf (at , code , format , args ...))
}
func (check *Checker ) softErrorf (at positioner , code Code , format string , args ...any ) {
err := newErrorf (at , code , format , args ...)
err .soft = true
check .report (err )
}
func (check *Checker ) versionErrorf (at positioner , v version , format string , args ...interface {}) {
msg := check .sprintf (format , args ...)
var err *error_
err = newErrorf (at , UnsupportedFeature , "%s requires %s or later" , msg , v )
check .report (err )
}
type positioner interface {
Pos() token .Pos
}
type posSpan struct {
start, pos, end token .Pos
}
func (e posSpan ) Pos () token .Pos {
return e .pos
}
func inNode(node ast .Node , pos token .Pos ) posSpan {
start , end := node .Pos (), node .End ()
if debug {
assert (start <= pos && pos < end )
}
return posSpan {start , pos , end }
}
type atPos token .Pos
func (s atPos ) Pos () token .Pos {
return token .Pos (s )
}
func spanOf(at positioner ) posSpan {
switch x := at .(type ) {
case nil :
panic ("nil positioner" )
case posSpan :
return x
case ast .Node :
pos := x .Pos ()
return posSpan {pos , pos , x .End ()}
case *operand :
if x .expr != nil {
pos := x .Pos ()
return posSpan {pos , pos , x .expr .End ()}
}
return posSpan {nopos , nopos , nopos }
default :
pos := at .Pos ()
return posSpan {pos , pos , pos }
}
}
func stripAnnotations(s string ) string {
var buf strings .Builder
for _ , r := range s {
if r < '₀' || '₀' +10 <= r {
buf .WriteRune (r )
}
}
if buf .Len () < len (s ) {
return buf .String ()
}
return s
}
The pages are generated with Golds v0.6.6 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .