diff --git a/go/types/api.go b/go/types/api.go index c240dcef..76643dcd 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -46,12 +46,15 @@ func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error return pkg, nil } -// An Error describes a type-checking error; -// it implements the error interface. +// An Error describes a type-checking error; it implements the error interface. +// A "soft" error is an error that still permits a valid interpretation of a +// package (such as "unused variable"); "hard" errors may lead to unpredictable +// behavior if ignored. type Error struct { Fset *token.FileSet // file set for interpretation of Pos Pos token.Pos // error position Msg string // error message + Soft bool // if set, error is "soft" } // Error returns an error string formatted as follows: diff --git a/go/types/errors.go b/go/types/errors.go index cbff0784..efe32d1c 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -54,8 +54,8 @@ func (check *checker) dump(format string, args ...interface{}) { fmt.Println(check.sprintf(format, args...)) } -func (check *checker) err(pos token.Pos, msg string) { - err := Error{check.fset, pos, msg} +func (check *checker) err(pos token.Pos, msg string, soft bool) { + err := Error{check.fset, pos, msg, soft} if check.firstErr == nil { check.firstErr = err } @@ -66,8 +66,16 @@ func (check *checker) err(pos token.Pos, msg string) { f(err) } +func (check *checker) error(pos token.Pos, msg string) { + check.err(pos, msg, false) +} + func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) { - check.err(pos, check.sprintf(format, args...)) + check.err(pos, check.sprintf(format, args...), false) +} + +func (check *checker) softErrorf(pos token.Pos, format string, args ...interface{}) { + check.err(pos, check.sprintf(format, args...), true) } func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) { diff --git a/go/types/expr.go b/go/types/expr.go index 83d83192..93a20140 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -961,7 +961,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { case *ast.Ellipsis: // ellipses are handled explicitly where they are legal // (array composite literals and parameter lists) - check.errorf(e.Pos(), "invalid use of '...'") + check.error(e.Pos(), "invalid use of '...'") goto Error case *ast.BasicLit: @@ -1005,7 +1005,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { } } if typ == nil { - check.errorf(e.Pos(), "missing type in composite literal") + check.error(e.Pos(), "missing type in composite literal") goto Error } @@ -1021,7 +1021,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { - check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") + check.error(e.Pos(), "mixture of field:value and value elements in struct literal") continue } key, _ := kv.Key.(*ast.Ident) @@ -1055,12 +1055,12 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // no element must have a key for i, e := range e.Elts { if kv, _ := e.(*ast.KeyValueExpr); kv != nil { - check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") + check.error(kv.Pos(), "mixture of field:value and value elements in struct literal") continue } check.expr(x, e) if i >= len(fields) { - check.errorf(x.pos(), "too many values in struct literal") + check.error(x.pos(), "too many values in struct literal") break // cannot continue } // i < len(fields) @@ -1073,7 +1073,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { } } if len(e.Elts) < len(fields) { - check.errorf(e.Rbrace, "too few values in struct literal") + check.error(e.Rbrace, "too few values in struct literal") // ok to continue } } @@ -1093,7 +1093,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { for _, e := range e.Elts { kv, _ := e.(*ast.KeyValueExpr) if kv == nil { - check.errorf(e.Pos(), "missing key in map literal") + check.error(e.Pos(), "missing key in map literal") continue } check.expr(x, kv.Key) @@ -1263,7 +1263,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // spec: "Only the first index may be omitted; it defaults to 0." if slice3(e) && (e.High == nil || sliceMax(e) == nil) { - check.errorf(e.Rbrack, "2nd and 3rd index required in 3-index slice") + check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice") goto Error } diff --git a/go/types/labels.go b/go/types/labels.go index d854031d..c1fab97f 100644 --- a/go/types/labels.go +++ b/go/types/labels.go @@ -35,7 +35,7 @@ func (check *checker) labels(body *ast.BlockStmt) { // spec: "It is illegal to define a label that is never used." for _, obj := range all.elems { if lbl := obj.(*Label); !lbl.used { - check.errorf(lbl.pos, "label %s declared but not used", lbl.name) + check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name) } } } diff --git a/go/types/resolver.go b/go/types/resolver.go index ae27e4b1..29525ead 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -311,8 +311,7 @@ func (check *checker) resolveFiles(files []*ast.File) { check.recordDef(d.Name, obj) // init functions must have a body if d.Body == nil { - check.errorf(obj.pos, "missing function body") - // ok to continue + check.softErrorf(obj.pos, "missing function body") } } else { check.declare(pkg.scope, d.Name, obj) @@ -414,7 +413,7 @@ func (check *checker) resolveFiles(files []*ast.File) { // Unused "blank imports" are automatically ignored // since _ identifiers are not entered into scopes. if !obj.used { - check.errorf(obj.pos, "%q imported but not used", obj.pkg.path) + check.softErrorf(obj.pos, "%q imported but not used", obj.pkg.path) } default: // All other objects in the file scope must be dot- @@ -432,7 +431,7 @@ func (check *checker) resolveFiles(files []*ast.File) { // check if the corresponding package was used. for pkg, pos := range dotImports[i] { if !usedDotImports[pkg] { - check.errorf(pos, "%q imported but not used", pkg.path) + check.softErrorf(pos, "%q imported but not used", pkg.path) } } } diff --git a/go/types/stmt.go b/go/types/stmt.go index 762279ff..ce3feff3 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -43,7 +43,7 @@ func (check *checker) funcBody(decl *declInfo, name string, sig *Signature, body } if sig.results.Len() > 0 && !check.isTerminating(body, "") { - check.errorf(body.Rbrace, "missing return") + check.error(body.Rbrace, "missing return") } // spec: "Implementation restriction: A compiler may make it illegal to @@ -56,7 +56,7 @@ func (check *checker) funcBody(decl *declInfo, name string, sig *Signature, body func (check *checker) usage(scope *Scope) { for _, obj := range scope.elems { if v, _ := obj.(*Var); v != nil && !v.used { - check.errorf(v.pos, "%s declared but not used", v.name) + check.softErrorf(v.pos, "%s declared but not used", v.name) } } for _, scope := range scope.children { @@ -186,7 +186,7 @@ L: for t, pos := range seen { if T == nil && t == nil || T != nil && t != nil && Identical(T, t) { // talk about "case" rather than "type" because of nil case - check.errorf(e.Pos(), "duplicate case in type switch") + check.error(e.Pos(), "duplicate case in type switch") check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented continue L } @@ -340,7 +340,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { check.initVars(res.vars, s.Results, s.Return) } } else if len(s.Results) > 0 { - check.errorf(s.Results[0].Pos(), "no result values expected") + check.error(s.Results[0].Pos(), "no result values expected") } case *ast.BranchStmt: @@ -351,15 +351,15 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { switch s.Tok { case token.BREAK: if ctxt&inBreakable == 0 { - check.errorf(s.Pos(), "break not in for, switch, or select statement") + check.error(s.Pos(), "break not in for, switch, or select statement") } case token.CONTINUE: if ctxt&inContinuable == 0 { - check.errorf(s.Pos(), "continue not in for statement") + check.error(s.Pos(), "continue not in for statement") } case token.FALLTHROUGH: if ctxt&fallthroughOk == 0 { - check.errorf(s.Pos(), "fallthrough statement out of place") + check.error(s.Pos(), "fallthrough statement out of place") } default: check.invalidAST(s.Pos(), "branch statement: %s", s.Tok) @@ -379,7 +379,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { var x operand check.expr(&x, s.Cond) if x.mode != invalid && !isBoolean(x.typ) { - check.errorf(s.Cond.Pos(), "non-boolean condition in if statement") + check.error(s.Cond.Pos(), "non-boolean condition in if statement") } check.stmt(inner, s.Body) if s.Else != nil { @@ -526,7 +526,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { v.used = true // avoid usage error when checking entire function } if !used { - check.errorf(lhs.Pos(), "%s declared but not used", lhs.Name) + check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name) } } @@ -563,7 +563,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { } if !valid { - check.errorf(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)") + check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)") continue } @@ -585,7 +585,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { var x operand check.expr(&x, s.Cond) if x.mode != invalid && !isBoolean(x.typ) { - check.errorf(s.Cond.Pos(), "non-boolean condition in for statement") + check.error(s.Cond.Pos(), "non-boolean condition in for statement") } } check.initStmt(s.Post) @@ -703,7 +703,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { check.declare(check.scope, nil, obj) // recordObject already called } } else { - check.errorf(s.TokPos, "no new variables on left side of :=") + check.error(s.TokPos, "no new variables on left side of :=") } } else { // ordinary assignment @@ -721,6 +721,6 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) { check.stmt(inner, s.Body) default: - check.errorf(s.Pos(), "invalid statement") + check.error(s.Pos(), "invalid statement") } }