errors.go raw

   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