github.com/bir3/gocompiler@v0.3.205/src/go/types/errors.go (about)

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