]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: report an error URL with error messages
authorRobert Griesemer <gri@golang.org>
Tue, 23 May 2023 22:50:13 +0000 (15:50 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 24 May 2023 00:18:17 +0000 (00:18 +0000)
In the type checkers, add Config.ErrorURL (or Config._ErrorURL for
go/types) to configure whether and how an error message should report
a URL for errors that have an error code.

In the compiler, configure types2 to report an error URL of the form
" [go.dev/e/XXX]", where XXX stands for the error code, with the URL
appended to the first line of an error.

Rename the compiler flag -url to -errorurl. At the moment this flag
is disabled by default.

Example for a one-line error message:

<pos>: undefined: f [go.dev/e/UndeclaredName]

Example for a multi-line error message:

<pos>: not enough arguments in call to min [go.dev/e/WrongArgCount]
have ()
want (P, P)

Change-Id: I26651ce2c92ad32fddd641f003db37fe12fdb1cd
Reviewed-on: https://go-review.googlesource.com/c/go/+/497715
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/base/flag.go
src/cmd/compile/internal/base/print.go
src/cmd/compile/internal/noder/irgen.go
src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/errors.go
src/go/types/api.go
src/go/types/api_test.go
src/go/types/check_test.go
src/go/types/errors.go

index bac421d3037befdce7104d2f150767531b7211fb..753a60ae1e591b53620295a431c22f14dba1a1dd 100644 (file)
@@ -124,7 +124,7 @@ type CmdFlags struct {
        TrimPath           string       "help:\"remove `prefix` from recorded source file paths\""
        WB                 bool         "help:\"enable write barrier\"" // TODO: remove
        PgoProfile         string       "help:\"read profile from `file`\""
-       Url                bool         "help:\"print explanatory URL with error message if applicable\""
+       ErrorURL           bool         "help:\"print explanatory URL with error message if applicable\""
 
        // Configuration derived from flags; not a flag itself.
        Cfg struct {
index 25ae04887f4e9b346df24b470aeee8fb585d85a3..efd70f7cc54c56b41d21efb83d180f4d879b8945 100644 (file)
@@ -85,11 +85,7 @@ func FlushErrors() {
        sort.Stable(byPos(errorMsgs))
        for i, err := range errorMsgs {
                if i == 0 || err.msg != errorMsgs[i-1].msg {
-                       fmt.Printf("%s", err.msg)
-                       if Flag.Url && err.code != 0 {
-                               // TODO(gri) we should come up with a better URL eventually
-                               fmt.Printf("\thttps://pkg.go.dev/internal/types/errors#%s\n", err.code)
-                       }
+                       fmt.Print(err.msg)
                }
        }
        errorMsgs = errorMsgs[:0]
index 3adf9e5d11d82bd4278d8c5d8ea4a2eb2e99dbd1..df5de63620ce87aba933b69a18de627e11dac66b 100644 (file)
@@ -53,6 +53,9 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
                Importer: &importer,
                Sizes:    &gcSizes{},
        }
+       if base.Flag.ErrorURL {
+               conf.ErrorURL = " [go.dev/e/%s]"
+       }
        info := &types2.Info{
                StoreTypesInSyntax: true,
                Defs:               make(map[*syntax.Name]types2.Object),
index b798f2c888e8f31ad30dc28a5bb5e0349659b001..63ef31ba84ef661207ce04a2b4d94b2404688bef 100644 (file)
@@ -169,6 +169,12 @@ type Config struct {
        // If DisableUnusedImportCheck is set, packages are not checked
        // for unused imports.
        DisableUnusedImportCheck bool
+
+       // If a non-empty ErrorURL format string is provided, it is used
+       // to format an error URL link that is appended to the first line
+       // of an error message. ErrorURL must be a format string containing
+       // exactly one "%s" format, e.g. "[go.dev/e/%s]".
+       ErrorURL string
 }
 
 func srcimporter_setUsesCgo(conf *Config) {
index f19b962116393ed05375a203a49f9e6d4b5a9171..bf807c35be1b443133999e5dbf0c9add874d7b76 100644 (file)
@@ -2661,3 +2661,28 @@ func (V4) M()
        // V4 has no method m but has M. Should not report wrongType.
        checkMissingMethod("V4", false)
 }
+
+func TestErrorURL(t *testing.T) {
+       conf := Config{ErrorURL: " [go.dev/e/%s]"}
+
+       // test case for a one-line error
+       const src1 = `
+package p
+var _ T
+`
+       _, err := typecheck(src1, &conf, nil)
+       if err == nil || !strings.HasSuffix(err.Error(), " [go.dev/e/UndeclaredName]") {
+               t.Errorf("src1: unexpected error: got %v", err)
+       }
+
+       // test case for a multi-line error
+       const src2 = `
+package p
+func f() int { return 0 }
+var _ = f(1, 2)
+`
+       _, err = typecheck(src2, &conf, nil)
+       if err == nil || !strings.Contains(err.Error(), " [go.dev/e/WrongArgCount]\n") {
+               t.Errorf("src1: unexpected error: got %v", err)
+       }
+}
index 1a9ab6909319b5219ab639d34c5feea57b63b3c2..7db06d944d8d08bb11009b6ddfe9f1ac22f68158 100644 (file)
@@ -250,6 +250,16 @@ func (check *Checker) err(at poser, code Code, msg string, soft bool) {
                pos = check.errpos
        }
 
+       // If we have an URL for error codes, add a link to the first line.
+       if code != 0 && check.conf.ErrorURL != "" {
+               u := fmt.Sprintf(check.conf.ErrorURL, code)
+               if i := strings.Index(msg, "\n"); i >= 0 {
+                       msg = msg[:i] + u + msg[i:]
+               } else {
+                       msg += u
+               }
+       }
+
        err := Error{pos, stripAnnotations(msg), msg, soft, code}
        if check.firstErr == nil {
                check.firstErr = err
index 08430c9e7a5c85c9ab1c23ccc1c70970a470db59..61d313c0e1af11bb2d0cf8b8051e190f684f130b 100644 (file)
@@ -170,6 +170,12 @@ type Config struct {
        // If DisableUnusedImportCheck is set, packages are not checked
        // for unused imports.
        DisableUnusedImportCheck bool
+
+       // If a non-empty _ErrorURL format string is provided, it is used
+       // to format an error URL link that is appended to the first line
+       // of an error message. ErrorURL must be a format string containing
+       // exactly one "%s" format, e.g. "[go.dev/e/%s]".
+       _ErrorURL string
 }
 
 func srcimporter_setUsesCgo(conf *Config) {
index 36d562a406f1f8d32d8811d1242445344c9db221..363e6d48e942f2c9442b19abc0f554b8c0ea10db 100644 (file)
@@ -2669,3 +2669,29 @@ func (V4) M()
        // V4 has no method m but has M. Should not report wrongType.
        checkMissingMethod("V4", false)
 }
+
+func TestErrorURL(t *testing.T) {
+       var conf Config
+       *stringFieldAddr(&conf, "_ErrorURL") = " [go.dev/e/%s]"
+
+       // test case for a one-line error
+       const src1 = `
+package p
+var _ T
+`
+       _, err := typecheck(src1, &conf, nil)
+       if err == nil || !strings.HasSuffix(err.Error(), " [go.dev/e/UndeclaredName]") {
+               t.Errorf("src1: unexpected error: got %v", err)
+       }
+
+       // test case for a multi-line error
+       const src2 = `
+package p
+func f() int { return 0 }
+var _ = f(1, 2)
+`
+       _, err = typecheck(src2, &conf, nil)
+       if err == nil || !strings.Contains(err.Error(), " [go.dev/e/WrongArgCount]\n") {
+               t.Errorf("src1: unexpected error: got %v", err)
+       }
+}
index 73ac80235cce010933f582608f37f189ab55d13e..9093a46a0a94a6754cf8f30864160ea11b0853a4 100644 (file)
@@ -300,6 +300,13 @@ func boolFieldAddr(conf *Config, name string) *bool {
        return (*bool)(v.FieldByName(name).Addr().UnsafePointer())
 }
 
+// stringFieldAddr(conf, name) returns the address of the string field conf.<name>.
+// For accessing unexported fields.
+func stringFieldAddr(conf *Config, name string) *string {
+       v := reflect.Indirect(reflect.ValueOf(conf))
+       return (*string)(v.FieldByName(name).Addr().UnsafePointer())
+}
+
 // TestManual is for manual testing of a package - either provided
 // as a list of filenames belonging to the package, or a directory
 // name containing the package files - after the test arguments
index 894403e66637605c36fe476699cf4f92969728f2..5cef8032cf3ebedf1621647cecdf36a167988513 100644 (file)
@@ -228,6 +228,16 @@ func (check *Checker) report(errp *error_) {
                panic("no error code provided")
        }
 
+       // If we have an URL for error codes, add a link to the first line.
+       if errp.code != 0 && check.conf._ErrorURL != "" {
+               u := fmt.Sprintf(check.conf._ErrorURL, errp.code)
+               if i := strings.Index(msg, "\n"); i >= 0 {
+                       msg = msg[:i] + u + msg[i:]
+               } else {
+                       msg += u
+               }
+       }
+
        span := spanOf(errp.desc[0].posn)
        e := Error{
                Fset:       check.fset,