github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/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 types2
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/go-asm/go/cmd/compile/syntax"
    17  	. "github.com/go-asm/go/types/errors"
    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  	pos    syntax.Pos
    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() syntax.Pos {
    56  	if err.empty() {
    57  		return nopos
    58  	}
    59  	return err.desc[0].pos
    60  }
    61  
    62  func (err *error_) msg(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.pos.IsKnown() {
    72  				fmt.Fprintf(&buf, "%s: ", p.pos)
    73  			}
    74  		}
    75  		buf.WriteString(sprintf(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("%s: %s", err.pos(), err.msg(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 poser, format string, args ...interface{}) {
    91  	err.desc = append(err.desc, errorDesc{atPos(at), format, args})
    92  }
    93  
    94  func sprintf(qf Qualifier, tpSubscripts bool, format string, args ...interface{}) string {
    95  	for i, arg := range args {
    96  		switch a := arg.(type) {
    97  		case nil:
    98  			arg = "<nil>"
    99  		case operand:
   100  			panic("got operand instead of *operand")
   101  		case *operand:
   102  			arg = operandString(a, qf)
   103  		case syntax.Pos:
   104  			arg = a.String()
   105  		case syntax.Expr:
   106  			arg = syntax.String(a)
   107  		case []syntax.Expr:
   108  			var buf strings.Builder
   109  			buf.WriteByte('[')
   110  			for i, x := range a {
   111  				if i > 0 {
   112  					buf.WriteString(", ")
   113  				}
   114  				buf.WriteString(syntax.String(x))
   115  			}
   116  			buf.WriteByte(']')
   117  			arg = buf.String()
   118  		case Object:
   119  			arg = ObjectString(a, qf)
   120  		case Type:
   121  			var buf bytes.Buffer
   122  			w := newTypeWriter(&buf, qf)
   123  			w.tpSubscripts = tpSubscripts
   124  			w.typ(a)
   125  			arg = buf.String()
   126  		case []Type:
   127  			var buf bytes.Buffer
   128  			w := newTypeWriter(&buf, qf)
   129  			w.tpSubscripts = tpSubscripts
   130  			buf.WriteByte('[')
   131  			for i, x := range a {
   132  				if i > 0 {
   133  					buf.WriteString(", ")
   134  				}
   135  				w.typ(x)
   136  			}
   137  			buf.WriteByte(']')
   138  			arg = buf.String()
   139  		case []*TypeParam:
   140  			var buf bytes.Buffer
   141  			w := newTypeWriter(&buf, qf)
   142  			w.tpSubscripts = tpSubscripts
   143  			buf.WriteByte('[')
   144  			for i, x := range a {
   145  				if i > 0 {
   146  					buf.WriteString(", ")
   147  				}
   148  				w.typ(x)
   149  			}
   150  			buf.WriteByte(']')
   151  			arg = buf.String()
   152  		}
   153  		args[i] = arg
   154  	}
   155  	return fmt.Sprintf(format, args...)
   156  }
   157  
   158  func (check *Checker) qualifier(pkg *Package) string {
   159  	// Qualify the package unless it's the package being type-checked.
   160  	if pkg != check.pkg {
   161  		if check.pkgPathMap == nil {
   162  			check.pkgPathMap = make(map[string]map[string]bool)
   163  			check.seenPkgMap = make(map[*Package]bool)
   164  			check.markImports(check.pkg)
   165  		}
   166  		// If the same package name was used by multiple packages, display the full path.
   167  		if len(check.pkgPathMap[pkg.name]) > 1 {
   168  			return strconv.Quote(pkg.path)
   169  		}
   170  		return pkg.name
   171  	}
   172  	return ""
   173  }
   174  
   175  // markImports recursively walks pkg and its imports, to record unique import
   176  // paths in pkgPathMap.
   177  func (check *Checker) markImports(pkg *Package) {
   178  	if check.seenPkgMap[pkg] {
   179  		return
   180  	}
   181  	check.seenPkgMap[pkg] = true
   182  
   183  	forName, ok := check.pkgPathMap[pkg.name]
   184  	if !ok {
   185  		forName = make(map[string]bool)
   186  		check.pkgPathMap[pkg.name] = forName
   187  	}
   188  	forName[pkg.path] = true
   189  
   190  	for _, imp := range pkg.imports {
   191  		check.markImports(imp)
   192  	}
   193  }
   194  
   195  // check may be nil.
   196  func (check *Checker) sprintf(format string, args ...interface{}) string {
   197  	var qf Qualifier
   198  	if check != nil {
   199  		qf = check.qualifier
   200  	}
   201  	return sprintf(qf, false, format, args...)
   202  }
   203  
   204  func (check *Checker) report(err *error_) {
   205  	if err.empty() {
   206  		panic("no error to report")
   207  	}
   208  	check.err(err.pos(), err.code, err.msg(check.qualifier), err.soft)
   209  }
   210  
   211  func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) {
   212  	fmt.Printf("%s:\t%s%s\n",
   213  		pos,
   214  		strings.Repeat(".  ", check.indent),
   215  		sprintf(check.qualifier, true, format, args...),
   216  	)
   217  }
   218  
   219  // dump is only needed for debugging
   220  func (check *Checker) dump(format string, args ...interface{}) {
   221  	fmt.Println(sprintf(check.qualifier, true, format, args...))
   222  }
   223  
   224  func (check *Checker) err(at poser, code Code, msg string, soft bool) {
   225  	switch code {
   226  	case InvalidSyntaxTree:
   227  		msg = "invalid syntax tree: " + msg
   228  	case 0:
   229  		panic("no error code provided")
   230  	}
   231  
   232  	// Cheap trick: Don't report errors with messages containing
   233  	// "invalid operand" or "invalid type" as those tend to be
   234  	// follow-on errors which don't add useful information. Only
   235  	// exclude them if these strings are not at the beginning,
   236  	// and only if we have at least one error already reported.
   237  	if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
   238  		return
   239  	}
   240  
   241  	pos := atPos(at)
   242  
   243  	// If we are encountering an error while evaluating an inherited
   244  	// constant initialization expression, pos is the position of in
   245  	// the original expression, and not of the currently declared
   246  	// constant identifier. Use the provided errpos instead.
   247  	// TODO(gri) We may also want to augment the error message and
   248  	// refer to the position (pos) in the original expression.
   249  	if check.errpos.IsKnown() {
   250  		assert(check.iota != nil)
   251  		pos = check.errpos
   252  	}
   253  
   254  	// If we have a URL for error codes, add a link to the first line.
   255  	if code != 0 && check.conf.ErrorURL != "" {
   256  		u := fmt.Sprintf(check.conf.ErrorURL, code)
   257  		if i := strings.Index(msg, "\n"); i >= 0 {
   258  			msg = msg[:i] + u + msg[i:]
   259  		} else {
   260  			msg += u
   261  		}
   262  	}
   263  
   264  	err := Error{pos, stripAnnotations(msg), msg, soft, code}
   265  	if check.firstErr == nil {
   266  		check.firstErr = err
   267  	}
   268  
   269  	if check.conf.Trace {
   270  		check.trace(pos, "ERROR: %s", msg)
   271  	}
   272  
   273  	f := check.conf.Error
   274  	if f == nil {
   275  		panic(bailout{}) // report only first error
   276  	}
   277  	f(err)
   278  }
   279  
   280  const (
   281  	invalidArg = "invalid argument: "
   282  	invalidOp  = "invalid operation: "
   283  )
   284  
   285  type poser interface {
   286  	Pos() syntax.Pos
   287  }
   288  
   289  func (check *Checker) error(at poser, code Code, msg string) {
   290  	check.err(at, code, msg, false)
   291  }
   292  
   293  func (check *Checker) errorf(at poser, code Code, format string, args ...interface{}) {
   294  	check.err(at, code, check.sprintf(format, args...), false)
   295  }
   296  
   297  func (check *Checker) softErrorf(at poser, code Code, format string, args ...interface{}) {
   298  	check.err(at, code, check.sprintf(format, args...), true)
   299  }
   300  
   301  func (check *Checker) versionErrorf(at poser, v goVersion, format string, args ...interface{}) {
   302  	msg := check.sprintf(format, args...)
   303  	msg = fmt.Sprintf("%s requires %s or later", msg, v)
   304  	check.err(at, UnsupportedFeature, msg, true)
   305  }
   306  
   307  // atPos reports the left (= start) position of at.
   308  func atPos(at poser) syntax.Pos {
   309  	switch x := at.(type) {
   310  	case *operand:
   311  		if x.expr != nil {
   312  			return syntax.StartPos(x.expr)
   313  		}
   314  	case syntax.Node:
   315  		return syntax.StartPos(x)
   316  	}
   317  	return at.Pos()
   318  }
   319  
   320  // stripAnnotations removes internal (type) annotations from s.
   321  func stripAnnotations(s string) string {
   322  	var buf strings.Builder
   323  	for _, r := range s {
   324  		// strip #'s and subscript digits
   325  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   326  			buf.WriteRune(r)
   327  		}
   328  	}
   329  	if buf.Len() < len(s) {
   330  		return buf.String()
   331  	}
   332  	return s
   333  }