github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/cmd/vet/example.go (about)

     1  // Copyright 2015 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 main
     6  
     7  import (
     8  	"go/ast"
     9  	"go/types"
    10  	"strings"
    11  	"unicode"
    12  	"unicode/utf8"
    13  )
    14  
    15  func init() {
    16  	register("example",
    17  		"check for common mistaken usages of documentation examples",
    18  		checkExample,
    19  		funcDecl)
    20  }
    21  
    22  func isExampleSuffix(s string) bool {
    23  	r, size := utf8.DecodeRuneInString(s)
    24  	return size > 0 && unicode.IsLower(r)
    25  }
    26  
    27  // checkExample walks the documentation example functions checking for common
    28  // mistakes of misnamed functions, failure to map functions to existing
    29  // identifiers, etc.
    30  func checkExample(f *File, node ast.Node) {
    31  	if !strings.HasSuffix(f.name, "_test.go") {
    32  		return
    33  	}
    34  	var (
    35  		pkg     = f.pkg
    36  		pkgName = pkg.typesPkg.Name()
    37  		scopes  = []*types.Scope{pkg.typesPkg.Scope()}
    38  		lookup  = func(name string) types.Object {
    39  			for _, scope := range scopes {
    40  				if o := scope.Lookup(name); o != nil {
    41  					return o
    42  				}
    43  			}
    44  			return nil
    45  		}
    46  	)
    47  	if strings.HasSuffix(pkgName, "_test") {
    48  		// Treat 'package foo_test' as an alias for 'package foo'.
    49  		var (
    50  			basePkg = strings.TrimSuffix(pkgName, "_test")
    51  			pkg     = f.pkg
    52  		)
    53  		for _, p := range pkg.typesPkg.Imports() {
    54  			if p.Name() == basePkg {
    55  				scopes = append(scopes, p.Scope())
    56  				break
    57  			}
    58  		}
    59  	}
    60  	fn, ok := node.(*ast.FuncDecl)
    61  	if !ok {
    62  		// Ignore non-functions.
    63  		return
    64  	}
    65  	var (
    66  		fnName = fn.Name.Name
    67  		report = func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
    68  	)
    69  	if fn.Recv != nil || !strings.HasPrefix(fnName, "Example") {
    70  		// Ignore methods and types not named "Example".
    71  		return
    72  	}
    73  	if params := fn.Type.Params; len(params.List) != 0 {
    74  		report("%s should be niladic", fnName)
    75  	}
    76  	if results := fn.Type.Results; results != nil && len(results.List) != 0 {
    77  		report("%s should return nothing", fnName)
    78  	}
    79  	if fnName == "Example" {
    80  		// Nothing more to do.
    81  		return
    82  	}
    83  	if filesRun && !includesNonTest {
    84  		// The coherence checks between a test and the package it tests
    85  		// will report false positives if no non-test files have
    86  		// been provided.
    87  		return
    88  	}
    89  	var (
    90  		exName = strings.TrimPrefix(fnName, "Example")
    91  		elems  = strings.SplitN(exName, "_", 3)
    92  		ident  = elems[0]
    93  		obj    = lookup(ident)
    94  	)
    95  	if ident != "" && obj == nil {
    96  		// Check ExampleFoo and ExampleBadFoo.
    97  		report("%s refers to unknown identifier: %s", fnName, ident)
    98  		// Abort since obj is absent and no subsequent checks can be performed.
    99  		return
   100  	}
   101  	if elemCnt := strings.Count(exName, "_"); elemCnt == 0 {
   102  		// Nothing more to do.
   103  		return
   104  	}
   105  	mmbr := elems[1]
   106  	if ident == "" {
   107  		// Check Example_suffix and Example_BadSuffix.
   108  		if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
   109  			report("%s has malformed example suffix: %s", fnName, residual)
   110  		}
   111  		return
   112  	}
   113  	if !isExampleSuffix(mmbr) {
   114  		// Check ExampleFoo_Method and ExampleFoo_BadMethod.
   115  		if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
   116  			report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
   117  		}
   118  	}
   119  	if len(elems) == 3 && !isExampleSuffix(elems[2]) {
   120  		// Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
   121  		report("%s has malformed example suffix: %s", fnName, elems[2])
   122  	}
   123  	return
   124  }