1
0
mirror of https://github.com/golang/go synced 2024-11-18 19:44:46 -07:00

go.tools/go/types: support for "soft" errors

Provide an extra field, "Soft" in types.Error.
A client may want to filter out soft errors and
only abort if there are "hard" errors.

Qualified a first set of errors as "soft".

Fixes golang/go#7360.

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/71800043
This commit is contained in:
Robert Griesemer 2014-03-05 15:19:38 -08:00
parent 5d3ec72759
commit d14a9d31e6
6 changed files with 41 additions and 31 deletions

View File

@ -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:

View File

@ -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{}) {

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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")
}
}