blob: fe51597b9bb7f317b7f4483ee0e26b383723f25b [file] [log] [blame]
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements various error reporters.
package types
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"strings"
)
// TODO(gri) eventually assert should disappear.
func assert(p bool) {
if !p {
panic("assertion failed")
}
}
func unreachable() {
panic("unreachable")
}
func (check *checker) formatMsg(format string, args []interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
args[i] = "<nil>"
case operand:
panic("internal error: should always pass *operand")
case token.Pos:
args[i] = check.fset.Position(a)
case ast.Expr:
args[i] = exprString(a)
}
}
return fmt.Sprintf(format, args...)
}
func (check *checker) trace(pos token.Pos, format string, args ...interface{}) {
fmt.Printf("%s:\t%s%s\n",
check.fset.Position(pos),
strings.Repeat(". ", check.indent),
check.formatMsg(format, args),
)
}
// dump is only needed for debugging
func (check *checker) dump(format string, args ...interface{}) {
fmt.Println(check.formatMsg(format, args))
}
func (check *checker) err(err error) {
if check.firstErr == nil {
check.firstErr = err
}
f := check.conf.Error
if f == nil {
panic(bailout{}) // report only first error
}
f(err)
}
func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) {
check.err(fmt.Errorf("%s: %s", check.fset.Position(pos), check.formatMsg(format, args)))
}
func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid AST: "+format, args...)
}
func (check *checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid argument: "+format, args...)
}
func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid operation: "+format, args...)
}
// exprString returns a (simplified) string representation for an expression.
func exprString(expr ast.Expr) string {
var buf bytes.Buffer
writeExpr(&buf, expr)
return buf.String()
}
// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a))
func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
switch x := expr.(type) {
case *ast.Ident:
buf.WriteString(x.Name)
case *ast.BasicLit:
buf.WriteString(x.Value)
case *ast.FuncLit:
buf.WriteString("(func literal)")
case *ast.CompositeLit:
buf.WriteString("(composite literal)")
case *ast.ParenExpr:
buf.WriteByte('(')
writeExpr(buf, x.X)
buf.WriteByte(')')
case *ast.SelectorExpr:
writeExpr(buf, x.X)
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)
case *ast.IndexExpr:
writeExpr(buf, x.X)
buf.WriteByte('[')
writeExpr(buf, x.Index)
buf.WriteByte(']')
case *ast.SliceExpr:
writeExpr(buf, x.X)
buf.WriteByte('[')
if x.Low != nil {
writeExpr(buf, x.Low)
}
buf.WriteByte(':')
if x.High != nil {
writeExpr(buf, x.High)
}
buf.WriteByte(']')
case *ast.TypeAssertExpr:
writeExpr(buf, x.X)
buf.WriteString(".(")
// TODO(gri) expand writeExpr so that types are not handled by default case
writeExpr(buf, x.Type)
buf.WriteByte(')')
case *ast.CallExpr:
writeExpr(buf, x.Fun)
buf.WriteByte('(')
for i, arg := range x.Args {
if i > 0 {
buf.WriteString(", ")
}
writeExpr(buf, arg)
}
buf.WriteByte(')')
case *ast.StarExpr:
buf.WriteByte('*')
writeExpr(buf, x.X)
case *ast.UnaryExpr:
buf.WriteString(x.Op.String())
writeExpr(buf, x.X)
case *ast.BinaryExpr:
// The AST preserves source-level parentheses so there is
// no need to introduce parentheses here for correctness.
writeExpr(buf, x.X)
buf.WriteByte(' ')
buf.WriteString(x.Op.String())
buf.WriteByte(' ')
writeExpr(buf, x.Y)
default:
// TODO(gri) Consider just calling x.String(). May cause
// infinite recursion if we missed a local type.
fmt.Fprintf(buf, "<expr %T>", x)
}
}
// typeString returns a string representation for typ.
func typeString(typ Type) string {
var buf bytes.Buffer
writeType(&buf, typ)
return buf.String()
}
func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) {
buf.WriteByte('(')
if tup != nil {
for i, v := range tup.vars {
if i > 0 {
buf.WriteString(", ")
}
if v.name != "" {
buf.WriteString(v.name)
buf.WriteByte(' ')
}
typ := v.typ
if isVariadic && i == len(tup.vars)-1 {
buf.WriteString("...")
typ = typ.(*Slice).elt
}
writeType(buf, typ)
}
}
buf.WriteByte(')')
}
func writeSignature(buf *bytes.Buffer, sig *Signature) {
writeTuple(buf, sig.params, sig.isVariadic)
n := sig.results.Len()
if n == 0 {
// no result
return
}
buf.WriteByte(' ')
if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result
writeType(buf, sig.results.vars[0].typ)
return
}
// multiple or named result(s)
writeTuple(buf, sig.results, false)
}
func writeType(buf *bytes.Buffer, typ Type) {
switch t := typ.(type) {
case nil:
buf.WriteString("<nil>")
case *Basic:
buf.WriteString(t.name)
case *Array:
fmt.Fprintf(buf, "[%d]", t.len)
writeType(buf, t.elt)
case *Slice:
buf.WriteString("[]")
writeType(buf, t.elt)
case *Struct:
buf.WriteString("struct{")
for i, f := range t.fields {
if i > 0 {
buf.WriteString("; ")
}
if !f.anonymous {
buf.WriteString(f.name)
buf.WriteByte(' ')
}
writeType(buf, f.typ)
if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag)
}
}
buf.WriteByte('}')
case *Pointer:
buf.WriteByte('*')
writeType(buf, t.base)
case *Tuple:
writeTuple(buf, t, false)
case *Signature:
buf.WriteString("func")
writeSignature(buf, t)
case *Builtin:
fmt.Fprintf(buf, "<type of %s>", t.name)
case *Interface:
buf.WriteString("interface{")
for i, m := range t.methods {
if i > 0 {
buf.WriteString("; ")
}
buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature))
}
buf.WriteByte('}')
case *Map:
buf.WriteString("map[")
writeType(buf, t.key)
buf.WriteByte(']')
writeType(buf, t.elt)
case *Chan:
var s string
switch t.dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
s = "<-chan "
default:
s = "chan "
}
buf.WriteString(s)
writeType(buf, t.elt)
case *Named:
s := "<Named w/o object>"
if obj := t.obj; obj != nil {
if obj.pkg != nil {
// TODO(gri) Ideally we only want the qualification
// if we are referring to a type that was imported;
// but not when we are at the "top". We don't have
// this information easily available here.
buf.WriteString(obj.pkg.name)
buf.WriteByte('.')
}
s = t.obj.name
}
buf.WriteString(s)
default:
// For externally defined implementations of Type.
buf.WriteString(t.String())
}
}