...

Source file src/go/types/errors.go

Documentation: go/types

     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  	"go/ast"
    13  	"go/token"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  func assert(p bool) {
    20  	if !p {
    21  		msg := "assertion failed"
    22  		// Include information about the assertion location. Due to panic recovery,
    23  		// this location is otherwise buried in the middle of the panicking stack.
    24  		if _, file, line, ok := runtime.Caller(1); ok {
    25  			msg = fmt.Sprintf("%s:%d: %s", file, line, msg)
    26  		}
    27  		panic(msg)
    28  	}
    29  }
    30  
    31  func unreachable() {
    32  	panic("unreachable")
    33  }
    34  
    35  // An error_ represents a type-checking error.
    36  // To report an error_, call Checker.report.
    37  type error_ struct {
    38  	desc []errorDesc
    39  	code errorCode
    40  	soft bool // TODO(gri) eventually determine this from an error code
    41  }
    42  
    43  // An errorDesc describes part of a type-checking error.
    44  type errorDesc struct {
    45  	posn   positioner
    46  	format string
    47  	args   []interface{}
    48  }
    49  
    50  func (err *error_) empty() bool {
    51  	return err.desc == nil
    52  }
    53  
    54  func (err *error_) pos() token.Pos {
    55  	if err.empty() {
    56  		return token.NoPos
    57  	}
    58  	return err.desc[0].posn.Pos()
    59  }
    60  
    61  func (err *error_) msg(fset *token.FileSet, qf Qualifier) string {
    62  	if err.empty() {
    63  		return "no error"
    64  	}
    65  	var buf bytes.Buffer
    66  	for i := range err.desc {
    67  		p := &err.desc[i]
    68  		if i > 0 {
    69  			fmt.Fprint(&buf, "\n\t")
    70  			if p.posn.Pos().IsValid() {
    71  				fmt.Fprintf(&buf, "%s: ", fset.Position(p.posn.Pos()))
    72  			}
    73  		}
    74  		buf.WriteString(sprintf(fset, qf, false, p.format, p.args...))
    75  	}
    76  	return buf.String()
    77  }
    78  
    79  // String is for testing.
    80  func (err *error_) String() string {
    81  	if err.empty() {
    82  		return "no error"
    83  	}
    84  	return fmt.Sprintf("%d: %s", err.pos(), err.msg(nil, nil))
    85  }
    86  
    87  // errorf adds formatted error information to err.
    88  // It may be called multiple times to provide additional information.
    89  func (err *error_) errorf(at token.Pos, format string, args ...interface{}) {
    90  	err.desc = append(err.desc, errorDesc{atPos(at), format, args})
    91  }
    92  
    93  func (check *Checker) qualifier(pkg *Package) string {
    94  	// Qualify the package unless it's the package being type-checked.
    95  	if pkg != check.pkg {
    96  		if check.pkgPathMap == nil {
    97  			check.pkgPathMap = make(map[string]map[string]bool)
    98  			check.seenPkgMap = make(map[*Package]bool)
    99  			check.markImports(check.pkg)
   100  		}
   101  		// If the same package name was used by multiple packages, display the full path.
   102  		if len(check.pkgPathMap[pkg.name]) > 1 {
   103  			return strconv.Quote(pkg.path)
   104  		}
   105  		return pkg.name
   106  	}
   107  	return ""
   108  }
   109  
   110  // markImports recursively walks pkg and its imports, to record unique import
   111  // paths in pkgPathMap.
   112  func (check *Checker) markImports(pkg *Package) {
   113  	if check.seenPkgMap[pkg] {
   114  		return
   115  	}
   116  	check.seenPkgMap[pkg] = true
   117  
   118  	forName, ok := check.pkgPathMap[pkg.name]
   119  	if !ok {
   120  		forName = make(map[string]bool)
   121  		check.pkgPathMap[pkg.name] = forName
   122  	}
   123  	forName[pkg.path] = true
   124  
   125  	for _, imp := range pkg.imports {
   126  		check.markImports(imp)
   127  	}
   128  }
   129  
   130  // check may be nil.
   131  func (check *Checker) sprintf(format string, args ...any) string {
   132  	var fset *token.FileSet
   133  	var qf Qualifier
   134  	if check != nil {
   135  		fset = check.fset
   136  		qf = check.qualifier
   137  	}
   138  	return sprintf(fset, qf, false, format, args...)
   139  }
   140  
   141  func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {
   142  	for i, arg := range args {
   143  		switch a := arg.(type) {
   144  		case nil:
   145  			arg = "<nil>"
   146  		case operand:
   147  			panic("got operand instead of *operand")
   148  		case *operand:
   149  			arg = operandString(a, qf)
   150  		case token.Pos:
   151  			if fset != nil {
   152  				arg = fset.Position(a).String()
   153  			}
   154  		case ast.Expr:
   155  			arg = ExprString(a)
   156  		case []ast.Expr:
   157  			var buf bytes.Buffer
   158  			buf.WriteByte('[')
   159  			writeExprList(&buf, a)
   160  			buf.WriteByte(']')
   161  			arg = buf.String()
   162  		case Object:
   163  			arg = ObjectString(a, qf)
   164  		case Type:
   165  			arg = typeString(a, qf, debug)
   166  		case []Type:
   167  			var buf bytes.Buffer
   168  			buf.WriteByte('[')
   169  			for i, x := range a {
   170  				if i > 0 {
   171  					buf.WriteString(", ")
   172  				}
   173  				buf.WriteString(typeString(x, qf, debug))
   174  			}
   175  			buf.WriteByte(']')
   176  			arg = buf.String()
   177  		case []*TypeParam:
   178  			var buf bytes.Buffer
   179  			buf.WriteByte('[')
   180  			for i, x := range a {
   181  				if i > 0 {
   182  					buf.WriteString(", ")
   183  				}
   184  				buf.WriteString(typeString(x, qf, debug)) // use typeString so we get subscripts when debugging
   185  			}
   186  			buf.WriteByte(']')
   187  			arg = buf.String()
   188  		}
   189  		args[i] = arg
   190  	}
   191  	return fmt.Sprintf(format, args...)
   192  }
   193  
   194  func (check *Checker) trace(pos token.Pos, format string, args ...any) {
   195  	fmt.Printf("%s:\t%s%s\n",
   196  		check.fset.Position(pos),
   197  		strings.Repeat(".  ", check.indent),
   198  		sprintf(check.fset, check.qualifier, true, format, args...),
   199  	)
   200  }
   201  
   202  // dump is only needed for debugging
   203  func (check *Checker) dump(format string, args ...any) {
   204  	fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
   205  }
   206  
   207  // Report records the error pointed to by errp, setting check.firstError if
   208  // necessary.
   209  func (check *Checker) report(errp *error_) {
   210  	if errp.empty() {
   211  		panic("empty error details")
   212  	}
   213  
   214  	span := spanOf(errp.desc[0].posn)
   215  	e := Error{
   216  		Fset:       check.fset,
   217  		Pos:        span.pos,
   218  		Msg:        errp.msg(check.fset, check.qualifier),
   219  		Soft:       errp.soft,
   220  		go116code:  errp.code,
   221  		go116start: span.start,
   222  		go116end:   span.end,
   223  	}
   224  
   225  	// Cheap trick: Don't report errors with messages containing
   226  	// "invalid operand" or "invalid type" as those tend to be
   227  	// follow-on errors which don't add useful information. Only
   228  	// exclude them if these strings are not at the beginning,
   229  	// and only if we have at least one error already reported.
   230  	isInvalidErr := strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0
   231  	if check.firstErr != nil && isInvalidErr {
   232  		return
   233  	}
   234  
   235  	e.Msg = stripAnnotations(e.Msg)
   236  	if check.errpos != nil {
   237  		// If we have an internal error and the errpos override is set, use it to
   238  		// augment our error positioning.
   239  		// TODO(rFindley) we may also want to augment the error message and refer
   240  		// to the position (pos) in the original expression.
   241  		span := spanOf(check.errpos)
   242  		e.Pos = span.pos
   243  		e.go116start = span.start
   244  		e.go116end = span.end
   245  	}
   246  	err := e
   247  
   248  	if check.firstErr == nil {
   249  		check.firstErr = err
   250  	}
   251  
   252  	if trace {
   253  		pos := e.Pos
   254  		msg := e.Msg
   255  		check.trace(pos, "ERROR: %s", msg)
   256  	}
   257  
   258  	f := check.conf.Error
   259  	if f == nil {
   260  		panic(bailout{}) // report only first error
   261  	}
   262  	f(err)
   263  }
   264  
   265  // newErrorf creates a new error_ for later reporting with check.report.
   266  func newErrorf(at positioner, code errorCode, format string, args ...any) *error_ {
   267  	return &error_{
   268  		desc: []errorDesc{{at, format, args}},
   269  		code: code,
   270  	}
   271  }
   272  
   273  func (check *Checker) error(at positioner, code errorCode, msg string) {
   274  	check.report(newErrorf(at, code, msg))
   275  }
   276  
   277  func (check *Checker) errorf(at positioner, code errorCode, format string, args ...any) {
   278  	check.report(newErrorf(at, code, format, args...))
   279  }
   280  
   281  func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...any) {
   282  	err := newErrorf(at, code, format, args...)
   283  	err.soft = true
   284  	check.report(err)
   285  }
   286  
   287  func (check *Checker) versionErrorf(at positioner, code errorCode, goVersion string, format string, args ...interface{}) {
   288  	msg := check.sprintf(format, args...)
   289  	var err *error_
   290  	if compilerErrorMessages {
   291  		err = newErrorf(at, code, "%s requires %s or later (-lang was set to %s; check go.mod)", msg, goVersion, check.conf.GoVersion)
   292  	} else {
   293  		err = newErrorf(at, code, "%s requires %s or later", msg, goVersion)
   294  	}
   295  	check.report(err)
   296  }
   297  
   298  func (check *Checker) invalidAST(at positioner, format string, args ...any) {
   299  	check.errorf(at, 0, "invalid AST: "+format, args...)
   300  }
   301  
   302  func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...any) {
   303  	check.errorf(at, code, "invalid argument: "+format, args...)
   304  }
   305  
   306  func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...any) {
   307  	check.errorf(at, code, "invalid operation: "+format, args...)
   308  }
   309  
   310  // The positioner interface is used to extract the position of type-checker
   311  // errors.
   312  type positioner interface {
   313  	Pos() token.Pos
   314  }
   315  
   316  // posSpan holds a position range along with a highlighted position within that
   317  // range. This is used for positioning errors, with pos by convention being the
   318  // first position in the source where the error is known to exist, and start
   319  // and end defining the full span of syntax being considered when the error was
   320  // detected. Invariant: start <= pos < end || start == pos == end.
   321  type posSpan struct {
   322  	start, pos, end token.Pos
   323  }
   324  
   325  func (e posSpan) Pos() token.Pos {
   326  	return e.pos
   327  }
   328  
   329  // inNode creates a posSpan for the given node.
   330  // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
   331  // first byte after node within the source).
   332  func inNode(node ast.Node, pos token.Pos) posSpan {
   333  	start, end := node.Pos(), node.End()
   334  	if debug {
   335  		assert(start <= pos && pos < end)
   336  	}
   337  	return posSpan{start, pos, end}
   338  }
   339  
   340  // atPos wraps a token.Pos to implement the positioner interface.
   341  type atPos token.Pos
   342  
   343  func (s atPos) Pos() token.Pos {
   344  	return token.Pos(s)
   345  }
   346  
   347  // spanOf extracts an error span from the given positioner. By default this is
   348  // the trivial span starting and ending at pos, but this span is expanded when
   349  // the argument naturally corresponds to a span of source code.
   350  func spanOf(at positioner) posSpan {
   351  	switch x := at.(type) {
   352  	case nil:
   353  		panic("nil positioner")
   354  	case posSpan:
   355  		return x
   356  	case ast.Node:
   357  		pos := x.Pos()
   358  		return posSpan{pos, pos, x.End()}
   359  	case *operand:
   360  		if x.expr != nil {
   361  			pos := x.Pos()
   362  			return posSpan{pos, pos, x.expr.End()}
   363  		}
   364  		return posSpan{token.NoPos, token.NoPos, token.NoPos}
   365  	default:
   366  		pos := at.Pos()
   367  		return posSpan{pos, pos, pos}
   368  	}
   369  }
   370  
   371  // stripAnnotations removes internal (type) annotations from s.
   372  func stripAnnotations(s string) string {
   373  	var b strings.Builder
   374  	for _, r := range s {
   375  		// strip #'s and subscript digits
   376  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   377  			b.WriteRune(r)
   378  		}
   379  	}
   380  	if b.Len() < len(s) {
   381  		return b.String()
   382  	}
   383  	return s
   384  }
   385  

View as plain text