github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/types/errors/generrordocs.go (about) 1 //go:build ignore 2 3 // Copyright 2023 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 // generrordocs creates a Markdown file for each (compiler) error code 8 // and its associated documentation. 9 // Note: this program must be run in this directory. 10 // go run generrordocs.go <dir> 11 12 //go:generate go run generrordocs.go errors_markdown 13 14 package main 15 16 import ( 17 "bytes" 18 "fmt" 19 "go/ast" 20 "go/importer" 21 "go/parser" 22 "go/token" 23 "log" 24 "os" 25 "path" 26 "strings" 27 "text/template" 28 29 . "go/types" 30 ) 31 32 func main() { 33 if len(os.Args) != 2 { 34 log.Fatal("missing argument: generrordocs <dir>") 35 } 36 outDir := os.Args[1] 37 if err := os.MkdirAll(outDir, 0755); err != nil { 38 log.Fatal("unable to create output directory: %s", err) 39 } 40 walkCodes(func(name string, vs *ast.ValueSpec) { 41 // ignore unused errors 42 if name == "_" { 43 return 44 } 45 // Ensure that < are represented correctly when its included in code 46 // blocks. The goldmark Markdown parser converts them to &lt; 47 // when not escaped. It is the only known string with this issue. 48 desc := strings.ReplaceAll(vs.Doc.Text(), "<", `{{raw "<"}}`) 49 e := struct { 50 Name string 51 Description string 52 }{ 53 Name: name, 54 Description: fmt.Sprintf("```\n%s```\n", desyc), 55 } 56 var buf bytes.Buffer 57 err := template.Must(template.New("eachError").Parse(markdownTemplate)).Execute(&buf, e) 58 if err != nil { 59 log.Fatalf("template.Must: %s", err) 60 } 61 if err := os.WriteFile(path.Join(outDir, name+".md"), buf.Bytes(), 0660); err != nil { 62 log.Fatalf("os.WriteFile: %s\n", err) 63 } 64 }) 65 log.Printf("output directory: %s\n", outDir) 66 } 67 68 func walkCodes(f func(string, *ast.ValueSpec)) { 69 fset := token.NewFileSet() 70 file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments) 71 if err != nil { 72 log.Fatalf("ParseFile failed: %s", err) 73 } 74 conf := Config{Importer: importer.Default()} 75 info := &Info{ 76 Types: make(map[ast.Expr]TypeAndValue), 77 Defs: make(map[*ast.Ident]Object), 78 Uses: make(map[*ast.Ident]Object), 79 } 80 _, err = conf.Check("types", fset, []*ast.File{file}, info) 81 if err != nil { 82 log.Fatalf("Check failed: %s", err) 83 } 84 for _, decl := range file.Decls { 85 decl, ok := decl.(*ast.GenDecl) 86 if !ok || decl.Tok != token.CONST { 87 continue 88 } 89 for _, spec := range decl.Specs { 90 spec, ok := spec.(*ast.ValueSpec) 91 if !ok || len(spec.Names) == 0 { 92 continue 93 } 94 obj := info.ObjectOf(spec.Names[0]) 95 if named, ok := obj.Type().(*Named); ok && named.Obj().Name() == "Code" { 96 if len(spec.Names) != 1 { 97 log.Fatalf("bad Code declaration for %q: got %d names, want exactly 1", spec.Names[0].Name, len(spec.Names)) 98 } 99 codename := spec.Names[0].Name 100 f(codename, spec) 101 } 102 } 103 } 104 } 105 106 const markdownTemplate = `--- 107 title: {{.Name}} 108 layout: article 109 --- 110 <!-- Copyright 2023 The Go Authors. All rights reserved. 111 Use of this source code is governed by a BSD-style 112 license that can be found in the LICENSE file. --> 113 114 <!-- Code generated by generrordocs.go; DO NOT EDIT. --> 115 116 {{.Description}} 117 `