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 7package types 8 9import ( 10 "errors" 11 "fmt" 12 "go/ast" 13 "go/token" 14 "strconv" 15 "strings" 16) 17 18func assert(p bool) { 19 if !p { 20 panic("assertion failed") 21 } 22} 23 24func unreachable() { 25 panic("unreachable") 26} 27 28func (check *Checker) qualifier(pkg *Package) string { 29 // Qualify the package unless it's the package being type-checked. 30 if pkg != check.pkg { 31 // If the same package name was used by multiple packages, display the full path. 32 if check.pkgCnt[pkg.name] > 1 { 33 return strconv.Quote(pkg.path) 34 } 35 return pkg.name 36 } 37 return "" 38} 39 40func (check *Checker) sprintf(format string, args ...interface{}) string { 41 for i, arg := range args { 42 switch a := arg.(type) { 43 case nil: 44 arg = "<nil>" 45 case operand: 46 panic("internal error: should always pass *operand") 47 case *operand: 48 arg = operandString(a, check.qualifier) 49 case token.Pos: 50 arg = check.fset.Position(a).String() 51 case ast.Expr: 52 arg = ExprString(a) 53 case Object: 54 arg = ObjectString(a, check.qualifier) 55 case Type: 56 arg = TypeString(a, check.qualifier) 57 } 58 args[i] = arg 59 } 60 return fmt.Sprintf(format, args...) 61} 62 63func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) { 64 fmt.Printf("%s:\t%s%s\n", 65 check.fset.Position(pos), 66 strings.Repeat(". ", check.indent), 67 check.sprintf(format, args...), 68 ) 69} 70 71// dump is only needed for debugging 72func (check *Checker) dump(format string, args ...interface{}) { 73 fmt.Println(check.sprintf(format, args...)) 74} 75 76func (check *Checker) err(err error) { 77 if err == nil { 78 return 79 } 80 var e Error 81 isInternal := errors.As(err, &e) 82 // Cheap trick: Don't report errors with messages containing 83 // "invalid operand" or "invalid type" as those tend to be 84 // follow-on errors which don't add useful information. Only 85 // exclude them if these strings are not at the beginning, 86 // and only if we have at least one error already reported. 87 isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0) 88 if check.firstErr != nil && isInvalidErr { 89 return 90 } 91 92 if check.errpos != nil && isInternal { 93 // If we have an internal error and the errpos override is set, use it to 94 // augment our error positioning. 95 // TODO(rFindley) we may also want to augment the error message and refer 96 // to the position (pos) in the original expression. 97 span := spanOf(check.errpos) 98 e.Pos = span.pos 99 e.go116start = span.start 100 e.go116end = span.end 101 err = e 102 } 103 104 if check.firstErr == nil { 105 check.firstErr = err 106 } 107 108 if trace { 109 pos := e.Pos 110 msg := e.Msg 111 if !isInternal { 112 msg = err.Error() 113 pos = token.NoPos 114 } 115 check.trace(pos, "ERROR: %s", msg) 116 } 117 118 f := check.conf.Error 119 if f == nil { 120 panic(bailout{}) // report only first error 121 } 122 f(err) 123} 124 125func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error { 126 span := spanOf(at) 127 return Error{ 128 Fset: check.fset, 129 Pos: span.pos, 130 Msg: msg, 131 Soft: soft, 132 go116code: code, 133 go116start: span.start, 134 go116end: span.end, 135 } 136} 137 138// newErrorf creates a new Error, but does not handle it. 139func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error { 140 msg := check.sprintf(format, args...) 141 return check.newError(at, code, soft, msg) 142} 143 144func (check *Checker) error(at positioner, code errorCode, msg string) { 145 check.err(check.newError(at, code, false, msg)) 146} 147 148func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) { 149 check.error(at, code, check.sprintf(format, args...)) 150} 151 152func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) { 153 check.err(check.newErrorf(at, code, true, format, args...)) 154} 155 156func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) { 157 check.errorf(at, 0, "invalid AST: "+format, args...) 158} 159 160func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) { 161 check.errorf(at, code, "invalid argument: "+format, args...) 162} 163 164func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) { 165 check.errorf(at, code, "invalid operation: "+format, args...) 166} 167 168// The positioner interface is used to extract the position of type-checker 169// errors. 170type positioner interface { 171 Pos() token.Pos 172} 173 174// posSpan holds a position range along with a highlighted position within that 175// range. This is used for positioning errors, with pos by convention being the 176// first position in the source where the error is known to exist, and start 177// and end defining the full span of syntax being considered when the error was 178// detected. Invariant: start <= pos < end || start == pos == end. 179type posSpan struct { 180 start, pos, end token.Pos 181} 182 183func (e posSpan) Pos() token.Pos { 184 return e.pos 185} 186 187// inNode creates a posSpan for the given node. 188// Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the 189// first byte after node within the source). 190func inNode(node ast.Node, pos token.Pos) posSpan { 191 start, end := node.Pos(), node.End() 192 if debug { 193 assert(start <= pos && pos < end) 194 } 195 return posSpan{start, pos, end} 196} 197 198// atPos wraps a token.Pos to implement the positioner interface. 199type atPos token.Pos 200 201func (s atPos) Pos() token.Pos { 202 return token.Pos(s) 203} 204 205// spanOf extracts an error span from the given positioner. By default this is 206// the trivial span starting and ending at pos, but this span is expanded when 207// the argument naturally corresponds to a span of source code. 208func spanOf(at positioner) posSpan { 209 switch x := at.(type) { 210 case nil: 211 panic("internal error: nil") 212 case posSpan: 213 return x 214 case ast.Node: 215 pos := x.Pos() 216 return posSpan{pos, pos, x.End()} 217 case *operand: 218 if x.expr != nil { 219 pos := x.Pos() 220 return posSpan{pos, pos, x.expr.End()} 221 } 222 return posSpan{token.NoPos, token.NoPos, token.NoPos} 223 default: 224 pos := at.Pos() 225 return posSpan{pos, pos, pos} 226 } 227} 228