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

View as plain text