Source file src/go/doc/doc.go

     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  	"go/ast"
    11  	"go/token"
    12  	"strings"
    13  )
    14  
    15  // Package is the documentation for an entire package.
    16  type Package struct {
    17  	Doc        string
    18  	Name       string
    19  	ImportPath string
    20  	Imports    []string
    21  	Filenames  []string
    22  	Notes      map[string][]*Note
    23  
    24  	// Deprecated: For backward compatibility Bugs is still populated,
    25  	// but all new code should use Notes instead.
    26  	Bugs []string
    27  
    28  	// declarations
    29  	Consts []*Value
    30  	Types  []*Type
    31  	Vars   []*Value
    32  	Funcs  []*Func
    33  
    34  	// Examples is a sorted list of examples associated with
    35  	// the package. Examples are extracted from _test.go files
    36  	// provided to NewFromFiles.
    37  	Examples []*Example
    38  }
    39  
    40  // Value is the documentation for a (possibly grouped) var or const declaration.
    41  type Value struct {
    42  	Doc   string
    43  	Names []string // var or const names in declaration order
    44  	Decl  *ast.GenDecl
    45  
    46  	order int
    47  }
    48  
    49  // Type is the documentation for a type declaration.
    50  type Type struct {
    51  	Doc  string
    52  	Name string
    53  	Decl *ast.GenDecl
    54  
    55  	// associated declarations
    56  	Consts  []*Value // sorted list of constants of (mostly) this type
    57  	Vars    []*Value // sorted list of variables of (mostly) this type
    58  	Funcs   []*Func  // sorted list of functions returning this type
    59  	Methods []*Func  // sorted list of methods (including embedded ones) of this type
    60  
    61  	// Examples is a sorted list of examples associated with
    62  	// this type. Examples are extracted from _test.go files
    63  	// provided to NewFromFiles.
    64  	Examples []*Example
    65  }
    66  
    67  // Func is the documentation for a func declaration.
    68  type Func struct {
    69  	Doc  string
    70  	Name string
    71  	Decl *ast.FuncDecl
    72  
    73  	// methods
    74  	// (for functions, these fields have the respective zero value)
    75  	Recv  string // actual   receiver "T" or "*T"
    76  	Orig  string // original receiver "T" or "*T"
    77  	Level int    // embedding level; 0 means not embedded
    78  
    79  	// Examples is a sorted list of examples associated with this
    80  	// function or method. Examples are extracted from _test.go files
    81  	// provided to NewFromFiles.
    82  	Examples []*Example
    83  }
    84  
    85  // A Note represents a marked comment starting with "MARKER(uid): note body".
    86  // Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
    87  // at least one character is recognized. The ":" following the uid is optional.
    88  // Notes are collected in the Package.Notes map indexed by the notes marker.
    89  type Note struct {
    90  	Pos, End token.Pos // position range of the comment containing the marker
    91  	UID      string    // uid found with the marker
    92  	Body     string    // note body text
    93  }
    94  
    95  // Mode values control the operation of New and NewFromFiles.
    96  type Mode int
    97  
    98  const (
    99  	// AllDecls says to extract documentation for all package-level
   100  	// declarations, not just exported ones.
   101  	AllDecls Mode = 1 << iota
   102  
   103  	// AllMethods says to show all embedded methods, not just the ones of
   104  	// invisible (unexported) anonymous fields.
   105  	AllMethods
   106  
   107  	// PreserveAST says to leave the AST unmodified. Originally, pieces of
   108  	// the AST such as function bodies were nil-ed out to save memory in
   109  	// godoc, but not all programs want that behavior.
   110  	PreserveAST
   111  )
   112  
   113  // New computes the package documentation for the given package AST.
   114  // New takes ownership of the AST pkg and may edit or overwrite it.
   115  // To have the Examples fields populated, use NewFromFiles and include
   116  // the package's _test.go files.
   117  //
   118  func New(pkg *ast.Package, importPath string, mode Mode) *Package {
   119  	var r reader
   120  	r.readPackage(pkg, mode)
   121  	r.computeMethodSets()
   122  	r.cleanupTypes()
   123  	return &Package{
   124  		Doc:        r.doc,
   125  		Name:       pkg.Name,
   126  		ImportPath: importPath,
   127  		Imports:    sortedKeys(r.imports),
   128  		Filenames:  r.filenames,
   129  		Notes:      r.notes,
   130  		Bugs:       noteBodies(r.notes["BUG"]),
   131  		Consts:     sortedValues(r.values, token.CONST),
   132  		Types:      sortedTypes(r.types, mode&AllMethods != 0),
   133  		Vars:       sortedValues(r.values, token.VAR),
   134  		Funcs:      sortedFuncs(r.funcs, true),
   135  	}
   136  }
   137  
   138  // NewFromFiles computes documentation for a package.
   139  //
   140  // The package is specified by a list of *ast.Files and corresponding
   141  // file set, which must not be nil.
   142  // NewFromFiles uses all provided files when computing documentation,
   143  // so it is the caller's responsibility to provide only the files that
   144  // match the desired build context. "go/build".Context.MatchFile can
   145  // be used for determining whether a file matches a build context with
   146  // the desired GOOS and GOARCH values, and other build constraints.
   147  // The import path of the package is specified by importPath.
   148  //
   149  // Examples found in _test.go files are associated with the corresponding
   150  // type, function, method, or the package, based on their name.
   151  // If the example has a suffix in its name, it is set in the
   152  // Example.Suffix field. Examples with malformed names are skipped.
   153  //
   154  // Optionally, a single extra argument of type Mode can be provided to
   155  // control low-level aspects of the documentation extraction behavior.
   156  //
   157  // NewFromFiles takes ownership of the AST files and may edit them,
   158  // unless the PreserveAST Mode bit is on.
   159  //
   160  func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...any) (*Package, error) {
   161  	// Check for invalid API usage.
   162  	if fset == nil {
   163  		panic(fmt.Errorf("doc.NewFromFiles: no token.FileSet provided (fset == nil)"))
   164  	}
   165  	var mode Mode
   166  	switch len(opts) { // There can only be 0 or 1 options, so a simple switch works for now.
   167  	case 0:
   168  		// Nothing to do.
   169  	case 1:
   170  		m, ok := opts[0].(Mode)
   171  		if !ok {
   172  			panic(fmt.Errorf("doc.NewFromFiles: option argument type must be doc.Mode"))
   173  		}
   174  		mode = m
   175  	default:
   176  		panic(fmt.Errorf("doc.NewFromFiles: there must not be more than 1 option argument"))
   177  	}
   178  
   179  	// Collect .go and _test.go files.
   180  	var (
   181  		goFiles     = make(map[string]*ast.File)
   182  		testGoFiles []*ast.File
   183  	)
   184  	for i := range files {
   185  		f := fset.File(files[i].Pos())
   186  		if f == nil {
   187  			return nil, fmt.Errorf("file files[%d] is not found in the provided file set", i)
   188  		}
   189  		switch name := f.Name(); {
   190  		case strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go"):
   191  			goFiles[name] = files[i]
   192  		case strings.HasSuffix(name, "_test.go"):
   193  			testGoFiles = append(testGoFiles, files[i])
   194  		default:
   195  			return nil, fmt.Errorf("file files[%d] filename %q does not have a .go extension", i, name)
   196  		}
   197  	}
   198  
   199  	// TODO(dmitshur,gri): A relatively high level call to ast.NewPackage with a simpleImporter
   200  	// ast.Importer implementation is made below. It might be possible to short-circuit and simplify.
   201  
   202  	// Compute package documentation.
   203  	pkg, _ := ast.NewPackage(fset, goFiles, simpleImporter, nil) // Ignore errors that can happen due to unresolved identifiers.
   204  	p := New(pkg, importPath, mode)
   205  	classifyExamples(p, Examples(testGoFiles...))
   206  	return p, nil
   207  }
   208  
   209  // simpleImporter returns a (dummy) package object named by the last path
   210  // component of the provided package path (as is the convention for packages).
   211  // This is sufficient to resolve package identifiers without doing an actual
   212  // import. It never returns an error.
   213  func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) {
   214  	pkg := imports[path]
   215  	if pkg == nil {
   216  		// note that strings.LastIndex returns -1 if there is no "/"
   217  		pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:])
   218  		pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import
   219  		imports[path] = pkg
   220  	}
   221  	return pkg, nil
   222  }
   223  

View as plain text