mirror of https://github.com/golang/go.git
go/types: first cut at better error messages
- added Error.Full and Error.FullError which provide the full error message, possibly containing internal details - Error.Msg and Error.Error provide a user-friendly error message Change-Id: Id3044165331af71be31ef423cd2c9b8fe28accbd
This commit is contained in:
parent
62683a7752
commit
6768c4f513
|
|
@ -41,7 +41,8 @@ import (
|
|||
type Error struct {
|
||||
Fset *token.FileSet // file set for interpretation of Pos
|
||||
Pos token.Pos // error position
|
||||
Msg string // error message
|
||||
Msg string // default error message, user-friendly
|
||||
Full string // full error message, for debugging (may contain internal details)
|
||||
Soft bool // if set, error is "soft"
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +52,13 @@ func (err Error) Error() string {
|
|||
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
|
||||
}
|
||||
|
||||
// FullError returns an error string like Error, buy it may contain
|
||||
// type-checker internal details such as subscript indices for type
|
||||
// parameters and more. Useful for debugging.
|
||||
func (err Error) FullError() string {
|
||||
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Full)
|
||||
}
|
||||
|
||||
// An Importer resolves import paths to Packages.
|
||||
//
|
||||
// CAUTION: This interface does not support the import of locally
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import (
|
|||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func assert(p bool) {
|
||||
|
|
@ -82,7 +84,7 @@ func (check *Checker) err(pos token.Pos, msg string, soft bool) {
|
|||
return
|
||||
}
|
||||
|
||||
err := Error{check.fset, pos, msg, soft}
|
||||
err := Error{check.fset, pos, cleanMsg(msg), msg, soft}
|
||||
if check.firstErr == nil {
|
||||
check.firstErr = err
|
||||
}
|
||||
|
|
@ -121,3 +123,54 @@ func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface
|
|||
func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
|
||||
check.errorf(pos, "invalid operation: "+format, args...)
|
||||
}
|
||||
|
||||
// cleanMsg removes subscripts and replaces <>'s in instantiated type names with ()'s.
|
||||
func cleanMsg(s string) string {
|
||||
var b strings.Builder
|
||||
var p rune // previous rune
|
||||
n := 0 // nesting level
|
||||
copy := false // indicates that we need a copy
|
||||
for i := 0; ; {
|
||||
r, w := utf8.DecodeRuneInString(s[i:])
|
||||
i += w
|
||||
if r == utf8.RuneError {
|
||||
if w == 0 {
|
||||
break // we're done
|
||||
}
|
||||
if w == 1 {
|
||||
continue // ignore (this should never happen)
|
||||
}
|
||||
}
|
||||
|
||||
// strip subscript digits
|
||||
if '₀' <= r && r < '₀'+10 { // '₀' == U+2080
|
||||
copy = true
|
||||
continue
|
||||
}
|
||||
|
||||
// replace <>'s in instantiated type names by ()'s
|
||||
// (use previous rune p and nesting level n for more
|
||||
// accurate replacement)
|
||||
if r == '<' && isIdentChar(p) {
|
||||
n++
|
||||
copy = true
|
||||
r = '('
|
||||
} else if r == '>' && n > 0 {
|
||||
n--
|
||||
copy = true
|
||||
r = ')'
|
||||
}
|
||||
|
||||
b.WriteRune(r)
|
||||
p = r
|
||||
}
|
||||
|
||||
if copy {
|
||||
return b.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func isIdentChar(r rune) bool {
|
||||
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2020 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.
|
||||
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCleanMsg(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"", ""},
|
||||
{" ", " "},
|
||||
{"foo", "foo"},
|
||||
{"foo<T>", "foo(T)"},
|
||||
{"foo <T>", "foo <T>"},
|
||||
{"foo << bar", "foo << bar"},
|
||||
{"foo₀", "foo"},
|
||||
{"foo<T₀>", "foo(T)"},
|
||||
} {
|
||||
got := cleanMsg(test.in)
|
||||
if got != test.want {
|
||||
t.Errorf("%q: got %q; want %q", test.in, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue