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

View as plain text