--- /dev/null
+//go:build ignore
+
+// Copyright 2023 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.
+
+// generrordocs creates a Markdown file for each (compiler) error code
+// and its associated documentation.
+// Note: this program must be run in this directory.
+// go run generrordocs.go <dir>
+
+//go:generate go run generrordocs.go errors_markdown
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "log"
+ "os"
+ "path"
+ "strings"
+ "text/template"
+
+ . "go/types"
+)
+
+func main() {
+ if len(os.Args) != 2 {
+ log.Fatal("missing argument: generrordocs <dir>")
+ }
+ outDir := os.Args[1]
+ if err := os.MkdirAll(outDir, 0755); err != nil {
+ log.Fatal("unable to create output directory: %s", err)
+ }
+ walkCodes(func(name string, vs *ast.ValueSpec) {
+ // ignore unused errors
+ if name == "_" {
+ return
+ }
+ // Ensure that < are represented correctly when its included in code
+ // blocks. The goldmark Markdown parser converts them to &lt;
+ // when not escaped. It is the only known string with this issue.
+ desc := strings.ReplaceAll(vs.Doc.Text(), "<", `{{raw "<"}}`)
+ e := struct {
+ Name string
+ Description string
+ }{
+ Name: name,
+ Description: fmt.Sprintf("```\n%s```\n", desyc),
+ }
+ var buf bytes.Buffer
+ err := template.Must(template.New("eachError").Parse(markdownTemplate)).Execute(&buf, e)
+ if err != nil {
+ log.Fatalf("template.Must: %s", err)
+ }
+ if err := os.WriteFile(path.Join(outDir, name+".md"), buf.Bytes(), 0660); err != nil {
+ log.Fatalf("os.WriteFile: %s\n", err)
+ }
+ })
+ log.Printf("output directory: %s\n", outDir)
+}
+
+func walkCodes(f func(string, *ast.ValueSpec)) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments)
+ if err != nil {
+ log.Fatalf("ParseFile failed: %s", err)
+ }
+ conf := Config{Importer: importer.Default()}
+ info := &Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ Defs: make(map[*ast.Ident]Object),
+ Uses: make(map[*ast.Ident]Object),
+ }
+ _, err = conf.Check("types", fset, []*ast.File{file}, info)
+ if err != nil {
+ log.Fatalf("Check failed: %s", err)
+ }
+ for _, decl := range file.Decls {
+ decl, ok := decl.(*ast.GenDecl)
+ if !ok || decl.Tok != token.CONST {
+ continue
+ }
+ for _, spec := range decl.Specs {
+ spec, ok := spec.(*ast.ValueSpec)
+ if !ok || len(spec.Names) == 0 {
+ continue
+ }
+ obj := info.ObjectOf(spec.Names[0])
+ if named, ok := obj.Type().(*Named); ok && named.Obj().Name() == "Code" {
+ if len(spec.Names) != 1 {
+ log.Fatalf("bad Code declaration for %q: got %d names, want exactly 1", spec.Names[0].Name, len(spec.Names))
+ }
+ codename := spec.Names[0].Name
+ f(codename, spec)
+ }
+ }
+ }
+}
+
+const markdownTemplate = `---
+title: {{.Name}}
+layout: article
+---
+<!-- Copyright 2023 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. -->
+
+<!-- Code generated by generrordocs.go; DO NOT EDIT. -->
+
+{{.Description}}
+`