Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/types/errors.go

Documentation: go/types

     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  	"errors"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/token"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  func assert(p bool) {
    19  	if !p {
    20  		panic("assertion failed")
    21  	}
    22  }
    23  
    24  func unreachable() {
    25  	panic("unreachable")
    26  }
    27  
    28  func (check *Checker) qualifier(pkg *Package) string {
    29  	// Qualify the package unless it's the package being type-checked.
    30  	if pkg != check.pkg {
    31  		if check.pkgPathMap == nil {
    32  			check.pkgPathMap = make(map[string]map[string]bool)
    33  			check.seenPkgMap = make(map[*Package]bool)
    34  			check.markImports(check.pkg)
    35  		}
    36  		// If the same package name was used by multiple packages, display the full path.
    37  		if len(check.pkgPathMap[pkg.name]) > 1 {
    38  			return strconv.Quote(pkg.path)
    39  		}
    40  		return pkg.name
    41  	}
    42  	return ""
    43  }
    44  
    45  // markImports recursively walks pkg and its imports, to record unique import
    46  // paths in pkgPathMap.
    47  func (check *Checker) markImports(pkg *Package) {
    48  	if check.seenPkgMap[pkg] {
    49  		return
    50  	}
    51  	check.seenPkgMap[pkg] = true
    52  
    53  	forName, ok := check.pkgPathMap[pkg.name]
    54  	if !ok {
    55  		forName = make(map[string]bool)
    56  		check.pkgPathMap[pkg.name] = forName
    57  	}
    58  	forName[pkg.path] = true
    59  
    60  	for _, imp := range pkg.imports {
    61  		check.markImports(imp)
    62  	}
    63  }
    64  
    65  func (check *Checker) sprintf(format string, args ...interface{}) string {
    66  	for i, arg := range args {
    67  		switch a := arg.(type) {
    68  		case nil:
    69  			arg = "<nil>"
    70  		case operand:
    71  			panic("internal error: should always pass *operand")
    72  		case *operand:
    73  			arg = operandString(a, check.qualifier)
    74  		case token.Pos:
    75  			arg = check.fset.Position(a).String()
    76  		case ast.Expr:
    77  			arg = ExprString(a)
    78  		case Object:
    79  			arg = ObjectString(a, check.qualifier)
    80  		case Type:
    81  			arg = TypeString(a, check.qualifier)
    82  		}
    83  		args[i] = arg
    84  	}
    85  	return fmt.Sprintf(format, args...)
    86  }
    87  
    88  func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
    89  	fmt.Printf("%s:\t%s%s\n",
    90  		check.fset.Position(pos),
    91  		strings.Repeat(".  ", check.indent),
    92  		check.sprintf(format, args...),
    93  	)
    94  }
    95  
    96  // dump is only needed for debugging
    97  func (check *Checker) dump(format string, args ...interface{}) {
    98  	fmt.Println(check.sprintf(format, args...))
    99  }
   100  
   101  func (check *Checker) err(err error) {
   102  	if err == nil {
   103  		return
   104  	}
   105  	var e Error
   106  	isInternal := errors.As(err, &e)
   107  	// Cheap trick: Don't report errors with messages containing
   108  	// "invalid operand" or "invalid type" as those tend to be
   109  	// follow-on errors which don't add useful information. Only
   110  	// exclude them if these strings are not at the beginning,
   111  	// and only if we have at least one error already reported.
   112  	isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0)
   113  	if check.firstErr != nil && isInvalidErr {
   114  		return
   115  	}
   116  
   117  	if isInternal {
   118  		e.Msg = stripAnnotations(e.Msg)
   119  		if check.errpos != nil {
   120  			// If we have an internal error and the errpos override is set, use it to
   121  			// augment our error positioning.
   122  			// TODO(rFindley) we may also want to augment the error message and refer
   123  			// to the position (pos) in the original expression.
   124  			span := spanOf(check.errpos)
   125  			e.Pos = span.pos
   126  			e.go116start = span.start
   127  			e.go116end = span.end
   128  		}
   129  		err = e
   130  	}
   131  
   132  	if check.firstErr == nil {
   133  		check.firstErr = err
   134  	}
   135  
   136  	if trace {
   137  		pos := e.Pos
   138  		msg := e.Msg
   139  		if !isInternal {
   140  			msg = err.Error()
   141  			pos = token.NoPos
   142  		}
   143  		check.trace(pos, "ERROR: %s", msg)
   144  	}
   145  
   146  	f := check.conf.Error
   147  	if f == nil {
   148  		panic(bailout{}) // report only first error
   149  	}
   150  	f(err)
   151  }
   152  
   153  func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
   154  	span := spanOf(at)
   155  	return Error{
   156  		Fset:       check.fset,
   157  		Pos:        span.pos,
   158  		Msg:        msg,
   159  		Soft:       soft,
   160  		go116code:  code,
   161  		go116start: span.start,
   162  		go116end:   span.end,
   163  	}
   164  }
   165  
   166  // newErrorf creates a new Error, but does not handle it.
   167  func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error {
   168  	msg := check.sprintf(format, args...)
   169  	return check.newError(at, code, soft, msg)
   170  }
   171  
   172  func (check *Checker) error(at positioner, code errorCode, msg string) {
   173  	check.err(check.newError(at, code, false, msg))
   174  }
   175  
   176  func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) {
   177  	check.error(at, code, check.sprintf(format, args...))
   178  }
   179  
   180  func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) {
   181  	check.err(check.newErrorf(at, code, true, format, args...))
   182  }
   183  
   184  func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) {
   185  	check.errorf(at, 0, "invalid AST: "+format, args...)
   186  }
   187  
   188  func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) {
   189  	check.errorf(at, code, "invalid argument: "+format, args...)
   190  }
   191  
   192  func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) {
   193  	check.errorf(at, code, "invalid operation: "+format, args...)
   194  }
   195  
   196  // The positioner interface is used to extract the position of type-checker
   197  // errors.
   198  type positioner interface {
   199  	Pos() token.Pos
   200  }
   201  
   202  // posSpan holds a position range along with a highlighted position within that
   203  // range. This is used for positioning errors, with pos by convention being the
   204  // first position in the source where the error is known to exist, and start
   205  // and end defining the full span of syntax being considered when the error was
   206  // detected. Invariant: start <= pos < end || start == pos == end.
   207  type posSpan struct {
   208  	start, pos, end token.Pos
   209  }
   210  
   211  func (e posSpan) Pos() token.Pos {
   212  	return e.pos
   213  }
   214  
   215  // inNode creates a posSpan for the given node.
   216  // Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
   217  // first byte after node within the source).
   218  func inNode(node ast.Node, pos token.Pos) posSpan {
   219  	start, end := node.Pos(), node.End()
   220  	if debug {
   221  		assert(start <= pos && pos < end)
   222  	}
   223  	return posSpan{start, pos, end}
   224  }
   225  
   226  // atPos wraps a token.Pos to implement the positioner interface.
   227  type atPos token.Pos
   228  
   229  func (s atPos) Pos() token.Pos {
   230  	return token.Pos(s)
   231  }
   232  
   233  // spanOf extracts an error span from the given positioner. By default this is
   234  // the trivial span starting and ending at pos, but this span is expanded when
   235  // the argument naturally corresponds to a span of source code.
   236  func spanOf(at positioner) posSpan {
   237  	switch x := at.(type) {
   238  	case nil:
   239  		panic("internal error: nil")
   240  	case posSpan:
   241  		return x
   242  	case ast.Node:
   243  		pos := x.Pos()
   244  		return posSpan{pos, pos, x.End()}
   245  	case *operand:
   246  		if x.expr != nil {
   247  			pos := x.Pos()
   248  			return posSpan{pos, pos, x.expr.End()}
   249  		}
   250  		return posSpan{token.NoPos, token.NoPos, token.NoPos}
   251  	default:
   252  		pos := at.Pos()
   253  		return posSpan{pos, pos, pos}
   254  	}
   255  }
   256  
   257  // stripAnnotations removes internal (type) annotations from s.
   258  func stripAnnotations(s string) string {
   259  	var b strings.Builder
   260  	for _, r := range s {
   261  		// strip #'s and subscript digits
   262  		if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
   263  			b.WriteRune(r)
   264  		}
   265  	}
   266  	if b.Len() < len(s) {
   267  		return b.String()
   268  	}
   269  	return s
   270  }
   271  

View as plain text