Commit 267a5539 authored by Kamil Kisiel's avatar Kamil Kisiel Committed by Andrew Gerrand

cmd/godoc: ignore misnamed examples and print a warning

Fixes #4211.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/6970051
parent c2d2bfcc
...@@ -317,18 +317,21 @@ func startsWithUppercase(s string) bool { ...@@ -317,18 +317,21 @@ func startsWithUppercase(s string) bool {
var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`) var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`)
// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name
// while keeping uppercase Braz in Foo_Braz.
func stripExampleSuffix(name string) string {
if i := strings.LastIndex(name, "_"); i != -1 {
if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
name = name[:i]
}
}
return name
}
func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string {
var buf bytes.Buffer var buf bytes.Buffer
for _, eg := range examples { for _, eg := range examples {
name := eg.Name name := stripExampleSuffix(eg.Name)
// Strip lowercase braz in Foo_braz or Foo_Bar_braz from name
// while keeping uppercase Braz in Foo_Braz.
if i := strings.LastIndex(name, "_"); i != -1 {
if i < len(name)-1 && !startsWithUppercase(name[i+1:]) {
name = name[:i]
}
}
if name != funcName { if name != funcName {
continue continue
...@@ -902,6 +905,82 @@ func packageExports(fset *token.FileSet, pkg *ast.Package) { ...@@ -902,6 +905,82 @@ func packageExports(fset *token.FileSet, pkg *ast.Package) {
} }
} }
// declNames returns the names declared by decl.
// Method names are returned in the form Receiver_Method.
func declNames(decl ast.Decl) (names []string) {
switch d := decl.(type) {
case *ast.FuncDecl:
name := d.Name.Name
if d.Recv != nil {
var typeName string
switch r := d.Recv.List[0].Type.(type) {
case *ast.StarExpr:
typeName = r.X.(*ast.Ident).Name
case *ast.Ident:
typeName = r.Name
}
name = typeName + "_" + name
}
names = []string{name}
case *ast.GenDecl:
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.TypeSpec:
names = append(names, s.Name.Name)
case *ast.ValueSpec:
for _, id := range s.Names {
names = append(names, id.Name)
}
}
}
}
return
}
// globalNames finds all top-level declarations in pkgs and returns a map
// with the identifier names as keys.
func globalNames(pkgs map[string]*ast.Package) map[string]bool {
names := make(map[string]bool)
for _, pkg := range pkgs {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
for _, name := range declNames(decl) {
names[name] = true
}
}
}
}
return names
}
// parseExamples gets examples for packages in pkgs from *_test.go files in dir.
func parseExamples(fset *token.FileSet, pkgs map[string]*ast.Package, dir string) ([]*doc.Example, error) {
var examples []*doc.Example
filter := func(d os.FileInfo) bool {
return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
}
testpkgs, err := parseDir(fset, dir, filter)
if err != nil {
return nil, err
}
globals := globalNames(pkgs)
for _, testpkg := range testpkgs {
var files []*ast.File
for _, f := range testpkg.Files {
files = append(files, f)
}
for _, e := range doc.Examples(files...) {
name := stripExampleSuffix(e.Name)
if name == "" || globals[name] {
examples = append(examples, e)
} else {
log.Printf("skipping example Example%s: refers to unknown function or type", e.Name)
}
}
}
return examples, nil
}
// getPageInfo returns the PageInfo for a package directory abspath. If the // getPageInfo returns the PageInfo for a package directory abspath. If the
// parameter genAST is set, an AST containing only the package exports is // parameter genAST is set, an AST containing only the package exports is
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
...@@ -975,21 +1054,9 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page ...@@ -975,21 +1054,9 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page
} }
} }
// get examples from *_test.go files examples, err := parseExamples(fset, pkgs, abspath)
var examples []*doc.Example if err != nil {
filter = func(d os.FileInfo) bool { log.Println("parsing examples:", err)
return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go")
}
if testpkgs, err := parseDir(fset, abspath, filter); err != nil {
log.Println("parsing test files:", err)
} else {
for _, testpkg := range testpkgs {
var files []*ast.File
for _, f := range testpkg.Files {
files = append(files, f)
}
examples = append(examples, doc.Examples(files...)...)
}
} }
// compute package documentation // compute package documentation
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment