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:
parent
5d3ec72759
commit
d14a9d31e6
@ -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:
|
||||
|
@ -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{}) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user