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 &amp;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  `