// Copyright 2012 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// This file implements error reporting.package typesimport (.)func assert( bool) {if ! { := "assertion failed"// Include information about the assertion location. Due to panic recovery, // this location is otherwise buried in the middle of the panicking stack.if , , , := runtime.Caller(1); { = fmt.Sprintf("%s:%d: %s", , , ) }panic() }}// An errorDesc describes part of a type-checking error.type errorDesc struct { posn positioner msg string}// An error_ represents a type-checking error.// A new error_ is created with Checker.newError.// To report an error_, call error_.report.type error_ struct { check *Checker desc []errorDesc code Code soft bool// TODO(gri) eventually determine this from an error code}// newError returns a new error_ with the given error code.func ( *Checker) ( Code) *error_ {if == 0 {panic("error code must not be 0") }return &error_{check: , code: }}// addf adds formatted error information to err.// It may be called multiple times to provide additional information.// The position of the first call to addf determines the position of the reported Error.// Subsequent calls to addf provide additional information in the form of additional lines// in the error message (types2) or continuation errors identified by a tab-indented error// message (go/types).func ( *error_) ( positioner, string, ...interface{}) { .desc = append(.desc, errorDesc{, .check.sprintf(, ...)})}// addAltDecl is a specialized form of addf reporting another declaration of obj.func ( *error_) ( Object) {if := .Pos(); .IsValid() {// We use "other" rather than "previous" here because // the first declaration seen may not be textually // earlier in the source. .addf(, "other declaration of %s", .Name()) }}func ( *error_) () bool {return .desc == nil}func ( *error_) () positioner {if .empty() {returnnoposn }return .desc[0].posn}// msg returns the formatted error message without the primary error position pos().func ( *error_) () string {if .empty() {return"no error" }varstrings.Builderfor := range .desc { := &.desc[]if > 0 {fmt.Fprint(&, "\n\t")if .posn.Pos().IsValid() {fmt.Fprintf(&, "%s: ", .check.fset.Position(.posn.Pos())) } } .WriteString(.msg) }return .String()}// report reports the error err, setting check.firstError if necessary.func ( *error_) () {if .empty() {panic("no error") }// Cheap trick: Don't report errors with messages containing // "invalid operand" or "invalid type" as those tend to be // follow-on errors which don't add useful information. Only // exclude them if these strings are not at the beginning, // and only if we have at least one error already reported. := .checkif .firstErr != nil {// It is sufficient to look at the first sub-error only. := .desc[0].msgifstrings.Index(, "invalid operand") > 0 || strings.Index(, "invalid type") > 0 {return } }if .conf._Trace { .trace(.posn().Pos(), "ERROR: %s (code = %d)", .desc[0].msg, .code) }// In go/types, if there is a sub-error with a valid position, // call the typechecker error handler for each sub-error. // Otherwise, call it once, with a single combined message. := falseif !isTypes2 {for := 1; < len(.desc); ++ {if .desc[].posn.Pos().IsValid() { = truebreak } } }if {for := range .desc { := &.desc[] .handleError(, .posn, .code, .msg, .soft) } } else { .handleError(0, .posn(), .code, .msg(), .soft) }// make sure the error is not reported twice .desc = nil}// handleError should only be called by error_.report.func ( *Checker) ( int, positioner, Code, string, bool) {assert( != 0)if == 0 {// If we are encountering an error while evaluating an inherited // constant initialization expression, pos is the position of // the original expression, and not of the currently declared // constant identifier. Use the provided errpos instead. // TODO(gri) We may also want to augment the error message and // refer to the position (pos) in the original expression.if .errpos != nil && .errpos.Pos().IsValid() {assert(.iota != nil) = .errpos }// Report invalid syntax trees explicitly.if == InvalidSyntaxTree { = "invalid syntax tree: " + }// If we have a URL for error codes, add a link to the first line.if .conf._ErrorURL != "" { := fmt.Sprintf(.conf._ErrorURL, )if := strings.Index(, "\n"); >= 0 { = [:] + + [:] } else { += } } } else {// Indent sub-error. // Position information is passed explicitly to Error, below. = "\t" + } := spanOf() := Error{Fset: .fset,Pos: .pos,Msg: stripAnnotations(),Soft: ,go116code: ,go116start: .start,go116end: .end, }if .errpos != nil {// If we have an internal error and the errpos override is set, use it to // augment our error positioning. // TODO(rFindley) we may also want to augment the error message and refer // to the position (pos) in the original expression. := spanOf(.errpos) .Pos = .pos .go116start = .start .go116end = .end }if .firstErr == nil { .firstErr = } := .conf.Errorif == nil {panic(bailout{}) // record first error and exit } ()}const ( invalidArg = "invalid argument: " invalidOp = "invalid operation: ")// The positioner interface is used to extract the position of type-checker errors.type positioner interface { Pos() token.Pos}func ( *Checker) ( positioner, Code, string) { := .newError() .addf(, "%s", ) .report()}func ( *Checker) ( positioner, Code, string, ...any) { := .newError() .addf(, , ...) .report()}func ( *Checker) ( positioner, Code, string, ...any) { := .newError() .addf(, , ...) .soft = true .report()}func ( *Checker) ( positioner, goVersion, string, ...any) { := .sprintf(, ...) := .newError(UnsupportedFeature) .addf(, "%s requires %s or later", , ) .report()}// atPos wraps a token.Pos to implement the positioner interface.type atPos token.Posfunc ( atPos) () token.Pos {returntoken.Pos()}// posSpan holds a position range along with a highlighted position within that// range. This is used for positioning errors, with pos by convention being the// first position in the source where the error is known to exist, and start// and end defining the full span of syntax being considered when the error was// detected. Invariant: start <= pos < end || start == pos == end.type posSpan struct { start, pos, end token.Pos}func ( posSpan) () token.Pos {return .pos}// inNode creates a posSpan for the given node.// Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the// first byte after node within the source).func inNode( ast.Node, token.Pos) posSpan { , := .Pos(), .End()ifdebug {assert( <= && < ) }returnposSpan{, , }}// spanOf extracts an error span from the given positioner. By default this is// the trivial span starting and ending at pos, but this span is expanded when// the argument naturally corresponds to a span of source code.func spanOf( positioner) posSpan {switch x := .(type) {casenil:panic("nil positioner")caseposSpan:returncaseast.Node: := .Pos()returnposSpan{, , .End()}case *operand:if .expr != nil { := .Pos()returnposSpan{, , .expr.End()} }returnposSpan{nopos, nopos, nopos}default: := .Pos()returnposSpan{, , } }}
The pages are generated with Goldsv0.6.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.