github.com/bir3/gocompiler@v0.9.2202/src/go/doc/doc.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package doc extracts source code documentation from a Go AST.
     6  package doc
     7  
     8  import (
     9  	"fmt"
    10  	"github.com/bir3/gocompiler/src/go/ast"
    11  	"github.com/bir3/gocompiler/src/go/doc/comment"
    12  	"github.com/bir3/gocompiler/src/go/token"
    13  	"strings"
    14  )
    15  
    16  // Package is the documentation for an entire package.
    17  type Package struct {
    18  	Doc		string
    19  	Name		string
    20  	ImportPath	string
    21  	Imports		[]string
    22  	Filenames	[]string
    23  	Notes		map[string][]*Note
    24  
    25  	// Deprecated: For backward compatibility Bugs is still populated,
    26  	// but all new code should use Notes instead.
    27  	Bugs	[]string
    28  
    29  	// declarations
    30  	Consts	[]*Value
    31  	Types	[]*Type
    32  	Vars	[]*Value
    33  	Funcs	[]*Func
    34  
    35  	// Examples is a sorted list of examples associated with
    36  	// the package. Examples are extracted from _test.go files
    37  	// provided to NewFromFiles.
    38  	Examples	[]*Example
    39  
    40  	importByName	map[string]string
    41  	syms		map[string]bool
    42  }
    43  
    44  // Value is the documentation for a (possibly grouped) var or const declaration.
    45  type Value struct {
    46  	Doc	string
    47  	Names	[]string	// var or const names in declaration order
    48  	Decl	*ast.GenDecl
    49  
    50  	order	int
    51  }
    52  
    53  // Type is the documentation for a type declaration.
    54  type Type struct {
    55  	Doc	string
    56  	Name	string
    57  	Decl	*ast.GenDecl
    58  
    59  	// associated declarations
    60  	Consts	[]*Value	// sorted list of constants of (mostly) this type
    61  	Vars	[]*Value	// sorted list of variables of (mostly) this type
    62  	Funcs	[]*Func		// sorted list of functions returning this type
    63  	Methods	[]*Func		// sorted list of methods (including embedded ones) of this type
    64  
    65  	// Examples is a sorted list of examples associated with
    66  	// this type. Examples are extracted from _test.go files
    67  	// provided to NewFromFiles.
    68  	Examples	[]*Example
    69  }
    70  
    71  // Func is the documentation for a func declaration.
    72  type Func struct {
    73  	Doc	string
    74  	Name	string
    75  	Decl	*ast.FuncDecl
    76  
    77  	// methods
    78  	// (for functions, these fields have the respective zero value)
    79  	Recv	string	// actual   receiver "T" or "*T" possibly followed by type parameters [P1, ..., Pn]
    80  	Orig	string	// original receiver "T" or "*T"
    81  	Level	int	// embedding level; 0 means not embedded
    82  
    83  	// Examples is a sorted list of examples associated with this
    84  	// function or method. Examples are extracted from _test.go files
    85  	// provided to NewFromFiles.
    86  	Examples	[]*Example
    87  }
    88  
    89  // A Note represents a marked comment starting with "MARKER(uid): note body".
    90  // Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
    91  // at least one character is recognized. The ":" following the uid is optional.
    92  // Notes are collected in the Package.Notes map indexed by the notes marker.
    93  type Note struct {
    94  	Pos, End	token.Pos	// position range of the comment containing the marker
    95  	UID		string		// uid found with the marker
    96  	Body		string		// note body text
    97  }
    98  
    99  // Mode values control the operation of [New] and [NewFromFiles].
   100  type Mode int
   101  
   102  const (
   103  	// AllDecls says to extract documentation for all package-level
   104  	// declarations, not just exported ones.
   105  	AllDecls	Mode	= 1 << iota
   106  
   107  	// AllMethods says to show all embedded methods, not just the ones of
   108  	// invisible (unexported) anonymous fields.
   109  	AllMethods
   110  
   111  	// PreserveAST says to leave the AST unmodified. Originally, pieces of
   112  	// the AST such as function bodies were nil-ed out to save memory in
   113  	// godoc, but not all programs want that behavior.
   114  	PreserveAST
   115  )
   116  
   117  // New computes the package documentation for the given package AST.
   118  // New takes ownership of the AST pkg and may edit or overwrite it.
   119  // To have the [Examples] fields populated, use [NewFromFiles] and include
   120  // the package's _test.go files.
   121  func New(pkg *ast.Package, importPath string, mode Mode) *Package {
   122  	var r reader
   123  	r.readPackage(pkg, mode)
   124  	r.computeMethodSets()
   125  	r.cleanupTypes()
   126  	p := &Package{
   127  		Doc:		r.doc,
   128  		Name:		pkg.Name,
   129  		ImportPath:	importPath,
   130  		Imports:	sortedKeys(r.imports),
   131  		Filenames:	r.filenames,
   132  		Notes:		r.notes,
   133  		Bugs:		noteBodies(r.notes["BUG"]),
   134  		Consts:		sortedValues(r.values, token.CONST),
   135  		Types:		sortedTypes(r.types, mode&AllMethods != 0),
   136  		Vars:		sortedValues(r.values, token.VAR),
   137  		Funcs:		sortedFuncs(r.funcs, true),
   138  
   139  		importByName:	r.importByName,
   140  		syms:		make(map[string]bool),
   141  	}
   142  
   143  	p.collectValues(p.Consts)
   144  	p.collectValues(p.Vars)
   145  	p.collectTypes(p.Types)
   146  	p.collectFuncs(p.Funcs)
   147  
   148  	return p
   149  }
   150  
   151  func (p *Package) collectValues(values []*Value) {
   152  	for _, v := range values {
   153  		for _, name := range v.Names {
   154  			p.syms[name] = true
   155  		}
   156  	}
   157  }
   158  
   159  func (p *Package) collectTypes(types []*Type) {
   160  	for _, t := range types {
   161  		if p.syms[t.Name] {
   162  			// Shouldn't be any cycles but stop just in case.
   163  			continue
   164  		}
   165  		p.syms[t.Name] = true
   166  		p.collectValues(t.Consts)
   167  		p.collectValues(t.Vars)
   168  		p.collectFuncs(t.Funcs)
   169  		p.collectFuncs(t.Methods)
   170  	}
   171  }
   172  
   173  func (p *Package) collectFuncs(funcs []*Func) {
   174  	for _, f := range funcs {
   175  		if f.Recv != "" {
   176  			r := strings.TrimPrefix(f.Recv, "*")
   177  			if i := strings.IndexByte(r, '['); i >= 0 {
   178  				r = r[:i]	// remove type parameters
   179  			}
   180  			p.syms[r+"."+f.Name] = true
   181  		} else {
   182  			p.syms[f.Name] = true
   183  		}
   184  	}
   185  }
   186  
   187  // NewFromFiles computes documentation for a package.
   188  //
   189  // The package is specified by a list of *ast.Files and corresponding
   190  // file set, which must not be nil.
   191  // NewFromFiles uses all provided files when computing documentation,
   192  // so it is the caller's responsibility to provide only the files that
   193  // match the desired build context. "go/build".Context.MatchFile can
   194  // be used for determining whether a file matches a build context with
   195  // the desired GOOS and GOARCH values, and other build constraints.
   196  // The import path of the package is specified by importPath.
   197  //
   198  // Examples found in _test.go files are associated with the corresponding
   199  // type, function, method, or the package, based on their name.
   200  // If the example has a suffix in its name, it is set in the
   201  // [Example.Suffix] field. [Examples] with malformed names are skipped.
   202  //
   203  // Optionally, a single extra argument of type [Mode] can be provided to
   204  // control low-level aspects of the documentation extraction behavior.
   205  //
   206  // NewFromFiles takes ownership of the AST files and may edit them,
   207  // unless the PreserveAST Mode bit is on.
   208  func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...any) (*Package, error) {
   209  	// Check for invalid API usage.
   210  	if fset == nil {
   211  		panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)"))
   212  	}
   213  	var mode Mode
   214  	switch len(opts) {	// There can only be 0 or 1 options, so a simple switch works for now.
   215  	case 0:
   216  		// Nothing to do.
   217  	case 1:
   218  		m, ok := opts[0].(Mode)
   219  		if !ok {
   220  			panic(fmt.Errorf("doc.NewFromFiles: option argument type must be doc.Mode"))
   221  		}
   222  		mode = m
   223  	default:
   224  		panic(fmt.Errorf("doc.NewFromFiles: there must not be more than 1 option argument"))
   225  	}
   226  
   227  	// Collect .go and _test.go files.
   228  	var (
   229  		goFiles		= make(map[string]*ast.File)
   230  		testGoFiles	[]*ast.File
   231  	)
   232  	for i := range files {
   233  		f := fset.File(files[i].Pos())
   234  		if f == nil {
   235  			return nil, fmt.Errorf("file files[%d] is not found in the provided file set", i)
   236  		}
   237  		switch name := f.Name(); {
   238  		case strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go"):
   239  			goFiles[name] = files[i]
   240  		case strings.HasSuffix(name, "_test.go"):
   241  			testGoFiles = append(testGoFiles, files[i])
   242  		default:
   243  			return nil, fmt.Errorf("file files[%d] filename %q does not have a .go extension", i, name)
   244  		}
   245  	}
   246  
   247  	// TODO(dmitshur,gri): A relatively high level call to ast.NewPackage with a simpleImporter
   248  	// ast.Importer implementation is made below. It might be possible to short-circuit and simplify.
   249  
   250  	// Compute package documentation.
   251  	pkg, _ := ast.NewPackage(fset, goFiles, simpleImporter, nil)	// Ignore errors that can happen due to unresolved identifiers.
   252  	p := New(pkg, importPath, mode)
   253  	classifyExamples(p, Examples(testGoFiles...))
   254  	return p, nil
   255  }
   256  
   257  // simpleImporter returns a (dummy) package object named by the last path
   258  // component of the provided package path (as is the convention for packages).
   259  // This is sufficient to resolve package identifiers without doing an actual
   260  // import. It never returns an error.
   261  func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
   262  	pkg := imports[path]
   263  	if pkg == nil {
   264  		// note that strings.LastIndex returns -1 if there is no "/"
   265  		pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
   266  		pkg.Data = ast.NewScope(nil)	// required by ast.NewPackage for dot-import
   267  		imports[path] = pkg
   268  	}
   269  	return pkg, nil
   270  }
   271  
   272  // lookupSym reports whether the package has a given symbol or method.
   273  //
   274  // If recv == "", HasSym reports whether the package has a top-level
   275  // const, func, type, or var named name.
   276  //
   277  // If recv != "", HasSym reports whether the package has a type
   278  // named recv with a method named name.
   279  func (p *Package) lookupSym(recv, name string) bool {
   280  	if recv != "" {
   281  		return p.syms[recv+"."+name]
   282  	}
   283  	return p.syms[name]
   284  }
   285  
   286  // lookupPackage returns the import path identified by name
   287  // in the given package. If name uniquely identifies a single import,
   288  // then lookupPackage returns that import.
   289  // If multiple packages are imported as name, importPath returns "", false.
   290  // Otherwise, if name is the name of p itself, importPath returns "", true,
   291  // to signal a reference to p.
   292  // Otherwise, importPath returns "", false.
   293  func (p *Package) lookupPackage(name string) (importPath string, ok bool) {
   294  	if path, ok := p.importByName[name]; ok {
   295  		if path == "" {
   296  			return "", false	// multiple imports used the name
   297  		}
   298  		return path, true	// found import
   299  	}
   300  	if p.Name == name {
   301  		return "", true	// allow reference to this package
   302  	}
   303  	return "", false	// unknown name
   304  }
   305  
   306  // Parser returns a doc comment parser configured
   307  // for parsing doc comments from package p.
   308  // Each call returns a new parser, so that the caller may
   309  // customize it before use.
   310  func (p *Package) Parser() *comment.Parser {
   311  	return &comment.Parser{
   312  		LookupPackage:	p.lookupPackage,
   313  		LookupSym:	p.lookupSym,
   314  	}
   315  }
   316  
   317  // Printer returns a doc comment printer configured
   318  // for printing doc comments from package p.
   319  // Each call returns a new printer, so that the caller may
   320  // customize it before use.
   321  func (p *Package) Printer() *comment.Printer {
   322  	// No customization today, but having p.Printer()
   323  	// gives us flexibility in the future, and it is convenient for callers.
   324  	return &comment.Printer{}
   325  }
   326  
   327  // HTML returns formatted HTML for the doc comment text.
   328  //
   329  // To customize details of the HTML, use [Package.Printer]
   330  // to obtain a [comment.Printer], and configure it
   331  // before calling its HTML method.
   332  func (p *Package) HTML(text string) []byte {
   333  	return p.Printer().HTML(p.Parser().Parse(text))
   334  }
   335  
   336  // Markdown returns formatted Markdown for the doc comment text.
   337  //
   338  // To customize details of the Markdown, use [Package.Printer]
   339  // to obtain a [comment.Printer], and configure it
   340  // before calling its Markdown method.
   341  func (p *Package) Markdown(text string) []byte {
   342  	return p.Printer().Markdown(p.Parser().Parse(text))
   343  }
   344  
   345  // Text returns formatted text for the doc comment text,
   346  // wrapped to 80 Unicode code points and using tabs for
   347  // code block indentation.
   348  //
   349  // To customize details of the formatting, use [Package.Printer]
   350  // to obtain a [comment.Printer], and configure it
   351  // before calling its Text method.
   352  func (p *Package) Text(text string) []byte {
   353  	return p.Printer().Text(p.Parser().Parse(text))
   354  }