Source file src/go/types/errors.go

     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 error reporting.
     6  
     7  package types
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	. "internal/types/errors"
    14  	"runtime"
    15  	"strings"
    16  )
    17  
    18  func assert(p bool) {
    19  	if !p {
    20  		msg := "assertion failed"
    21  		// Include information about the assertion location. Due to panic recovery,
    22  		// this location is otherwise buried in the middle of the panicking stack.
    23  		if _, file, line, ok := runtime.Caller(1); ok {
    24  			msg = fmt.Sprintf("%s:%d: %s", file, line, msg)
    25  		}
    26  		panic(msg)
    27  	}
    28  }
    29  
    30  // An errorDesc describes part of a type-checking error.
    31  type errorDesc struct {
    32  	posn positioner
    33  	msg  string
    34  }
    35  
    36  // An error_ represents a type-checking error.
    37  // A new error_ is created with Checker.newError.
    38  // To report an error_, call error_.report.
    39  type error_ struct {
    40  	check *Checker
    41  	desc  []errorDesc
    42  	code  Code
    43  	soft  bool // TODO(gri) eventually determine this from an error code
    44  }
    45  
    46  // newError returns a new error_ with the given error code.
    47  func (check *Checker) newError(code Code) *error_ {
    48  	if code == 0 {
    49  		panic("error code must not be 0")
    50  	}
    51  	return &error_{check: check, code: code}
    52  }
    53  
    54  // addf adds formatted error information to err.
    55  // It may be called multiple times to provide additional information.
    56  // The position of the first call to addf determines the position of the reported Error.
    57  // Subsequent calls to addf provide additional information in the form of additional lines
    58  // in the error message (types2) or continuation errors identified by a tab-indented error
    59  // message (go/types).
    60  func (err *error_) addf(at positioner, format string, args ...interface{}) {
    61  	err.desc = append(err.desc, errorDesc{at, err.check.sprintf(format, args...)})
    62  }
    63  
    64  // addAltDecl is a specialized form of addf reporting another declaration of obj.
    65  func (err *error_) addAltDecl(obj Object) {
    66  	if pos := obj.Pos(); pos.IsValid() {
    67  		// We use "other" rather than "previous" here because
    68  		// the first declaration seen may not be textually
    69  		// earlier in the source.
    70  		err.addf(obj, "other declaration of %s", obj.Name())
    71  	}
    72  }
    73  
    74  func (err *error_) empty() bool {
    75  	return err.desc == nil
    76  }
    77  
    78  func (err *error_) posn() positioner {
    79  	if err.empty() {
    80  		return noposn
    81  	}
    82  	return err.desc[0].posn
    83  }
    84  
    85  // msg returns the formatted error message without the primary error position pos().
    86  func (err *error_) msg() string {
    87  	if err.empty() {
    88  		return "no error"
    89  	}
    90  
    91  	var buf strings.Builder
    92  	for i := range err.desc {
    93  		p := &err.desc[i]
    94  		if i > 0 {
    95  			fmt.Fprint(&buf, "\n\t")
    96  			if p.posn.Pos().IsValid() {
    97  				fmt.Fprintf(&buf, "%s: ", err.check.fset.Position(p.posn.Pos()))
    98  			}
    99  		}
   100  		buf.WriteString(p.msg)
   101  	}
   102  	return buf.String()
   103  }
   104  
   105  // report reports the error err, setting check.firstError if necessary.
   106  func (err *error_) report() {
   107  	if err.empty() {
   108  		panic("no error")
   109  	}
   110  
   111  	// Cheap trick: Don't report errors with messages containing
   112  	// "invalid operand" or "invalid type" as those tend to be
   113  	// follow-on errors which don't add useful information. Only
   114  	// exclude them if these strings are not at the beginning,
   115  	// and only if we have at least one error already reported.
   116  	check := err.check
   117  	if check.firstErr != nil {
   118  		// It is sufficient to look at the first sub-error only.
   119  		msg := err.desc[0].msg
   120  		if strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0 {
   121  			return
   122  		}
   123  	}
   124  
   125  	if check.conf._Trace {
   126  		check.trace(err.posn().Pos(), "ERROR: %s (code = %d)", err.desc[0].msg, err.code)
   127  	}
   128  
   129  	// In go/types, if there is a sub-error with a valid position,
   130  	// call the typechecker error handler for each sub-error.
   131  	// Otherwise, call it once, with a single combined message.
   132  	multiError := false
   133  	if !isTypes2 {
   134  		for i := 1; i < len(err.desc); i++ {
   135  			if err.desc[i].posn.Pos().IsValid() {
   136  				multiError = true
   137  				break
   138  			}
   139  		}
   140  	}
   141  
   142  	if multiError {
   143  		for i := range err.desc {
   144  			p := &err.desc[i]
   145  			check.handleError(i, p.posn, err.code, p.msg, err.soft)
   146  		}
   147  	} else {
   148  		check.handleError(0, err.posn(), err.code, err.msg(), err.soft)
   149  	}
   150  
   151  	// make sure the error is not reported twice
   152  	err.desc = nil
   153  }
   154  
   155  // handleError should only be called by error_.report.
   156  func (check *Checker) handleError(index int, posn positioner, code Code, msg string, soft bool) {
   157  	assert(code != 0)
   158  
   159  	if index == 0 {
   160  		// If we are encountering an error while evaluating an inherited
   161  		// constant initialization expression, pos is the position of
   162  		// the original expression, and not of the currently declared
   163  		// constant identifier. Use the provided errpos instead.
   164  		// TODO(gri) We may also want to augment the error message and
   165  		// refer to the position (pos) in the original expression.
   166  		if check.errpos != nil && check.errpos.Pos().IsValid() {
   167  			assert(check.iota != nil)
   168  			posn = check.errpos
   169  		}
   170  
   171  		// Report invalid syntax trees explicitly.
   172  		if code == InvalidSyntaxTree {
   173  			msg = "invalid syntax tree: " + msg
   174  		}
   175  
   176  		// If we have a URL for error codes, add a link to the first line.
   177  		if check.conf._ErrorURL != "" {
   178  			url := fmt.Sprintf(check.conf._ErrorURL, code)
   179  			if i := strings.Index(msg, "\n"); i >= 0 {
   180  				msg = msg[:i] + url + msg[i:]
   181  			} else {
   182  				msg += url
   183  			}
   184  		}
   185  	} else {
   186  		// Indent sub-error.
   187  		// Position information is passed explicitly to Error, below.
   188  		msg = "\t" + msg
   189  	}
   190  
   191  	span := spanOf(posn)
   192  	e := Error{
   193  		Fset:       check.fset,
   194  		Pos:        span.pos,
   195  		Msg:        stripAnnotations(msg),
   196  		Soft:       soft,
   197  		go116code:  code,
   198  		go116start: span.start,
   199  		go116end:   span.end,
   200  	}
   201  
   202  	if check.errpos != nil {
   203  		// If we have an internal error and the errpos override is set, use it to
   204  		// augment our error positioning.
   205  		// TODO(rFindley) we may also want to augment the error message and refer
   206  		// to the position (pos) in the original expression.
   207  		span := spanOf(check.errpos)
   208  		e.Pos = span.pos
   209  		e.go116start = span.start
   210  		e.go116end = span.end
   211  	}
   212  
   213  	if check.firstErr == nil {
   214  		check.firstErr = e
   215  	}
   216  
   217  	f := check.conf.Error
   218  	if f == nil {
   219  		panic(bailout{}) // record first error and exit
   220  	}
   221  	f(e)
   222  }
   223  
   224  const (
   225  	invalidArg = "invalid argument: "
   226  	invalidOp  = "invalid operation: "
   227  )
   228  
   229  // The positioner interface is used to extract the position of type-checker errors.
   230  type positioner interface {
   231  	Pos() token.Pos
   232  }
   233  
   234  func (check *Checker) error(at positioner, code Code, msg string) {
   235  	err := check.newError(code)
   236  	err.addf(at, "%s", msg)
   237  	err.report()
   238  }
   239  
   240  func (check *Checker) errorf(at positioner, code Code, format string, args ...any) {
   241  	err := check.newError(code)
   242  	err.addf(at, format, args...)
   243  	err.report()
   244  }
   245  
   246  func (check *Checker) softErrorf(at positioner, code Code, format string, args ...any) {
   247  	err := check.newError(code)
   248  	err.addf(at, format, args...)
   249  	err.soft = true
   250  	err.report()
   251  }
   252  
   253  func (check *Checker) versionErrorf(at positioner, v goVersion, format string, args ...any) {
   254  	msg := check.sprintf(format, args...)
   255  	err := check.newError(UnsupportedFeature)
   256  	err.addf(at, "%s requires %s or later", msg, v)
   257  	err.report()
   258  }
   259  
   260  // atPos wraps a token.Pos to implement the positioner interface.
   261  type atPos token.Pos
   262  
   263  func (s atPos) Pos() token.Pos {
   264  	return token.Pos(s)
   265  }
   266  
   267  // posSpan holds a position range along with a highlighted position within that
   268  // range. This is used for positioning errors, with pos by convention being the
   269  // first position in the source where the error is known to exist, and start
   270  // and end defining the full span of syntax being considered when the error was
   271  // detected. Invariant: start <= pos < end || start == pos == end.
   272  type posSpan struct {
   273  	start, pos, end token.Pos
   274  }
   275  
   276  func (e posSpan) Pos() token.Pos {
   277  	return e.pos
   278  }
   279  
   280  // inNode creates a posSpan for the given node.
   281  // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
   282  // first byte after node within the source).
   283  func inNode(node ast.Node, pos token.Pos) posSpan {
   284  	start, end := node.Pos(), node.End()
   285  	if debug {
   286  		assert(start <= pos && pos < end)
   287  	}
   288  	return posSpan{start, pos, end}
   289  }
   290  
   291  // spanOf extracts an error span from the given positioner. By default this is
   292  // the trivial span starting and ending at pos, but this span is expanded when
   293  // the argument naturally corresponds to a span of source code.
   294  func spanOf(at positioner) posSpan {
   295  	switch x := at.(type) {
   296  	case nil:
   297  		panic("nil positioner")
   298  	case posSpan:
   299  		return x
   300  	case ast.Node:
   301  		pos := x.Pos()
   302  		return posSpan{pos, pos, x.End()}
   303  	case *operand:
   304  		if x.expr != nil {
   305  			pos := x.Pos()
   306  			return posSpan{pos, pos, x.expr.End()}
   307  		}
   308  		return posSpan{nopos, nopos, nopos}
   309  	default:
   310  		pos := at.Pos()
   311  		return posSpan{pos, pos, pos}
   312  	}
   313  }
   314  

View as plain text