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

View as plain text