| //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}} |
| ` |