gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/golang/lint/lint.go (about)

     1  // Copyright (c) 2013 The Go Authors. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file or at
     5  // https://developers.google.com/open-source/licenses/bsd.
     6  
     7  // Package lint contains a linter for Go source code.
     8  package lint
     9  
    10  import (
    11  	"bufio"
    12  	"bytes"
    13  	"fmt"
    14  	"go/ast"
    15  	"go/parser"
    16  	"go/printer"
    17  	"go/token"
    18  	"go/types"
    19  	"regexp"
    20  	"sort"
    21  	"strconv"
    22  	"strings"
    23  	"unicode"
    24  	"unicode/utf8"
    25  
    26  	"golang.org/x/tools/go/gcexportdata"
    27  )
    28  
    29  const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
    30  
    31  // A Linter lints Go source code.
    32  type Linter struct {
    33  }
    34  
    35  // Problem represents a problem in some source code.
    36  type Problem struct {
    37  	Position   token.Position // position in source file
    38  	Text       string         // the prose that describes the problem
    39  	Link       string         // (optional) the link to the style guide for the problem
    40  	Confidence float64        // a value in (0,1] estimating the confidence in this problem's correctness
    41  	LineText   string         // the source line
    42  	Category   string         // a short name for the general category of the problem
    43  
    44  	// If the problem has a suggested fix (the minority case),
    45  	// ReplacementLine is a full replacement for the relevant line of the source file.
    46  	ReplacementLine string
    47  }
    48  
    49  func (p *Problem) String() string {
    50  	if p.Link != "" {
    51  		return p.Text + "\n\n" + p.Link
    52  	}
    53  	return p.Text
    54  }
    55  
    56  type byPosition []Problem
    57  
    58  func (p byPosition) Len() int      { return len(p) }
    59  func (p byPosition) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
    60  
    61  func (p byPosition) Less(i, j int) bool {
    62  	pi, pj := p[i].Position, p[j].Position
    63  
    64  	if pi.Filename != pj.Filename {
    65  		return pi.Filename < pj.Filename
    66  	}
    67  	if pi.Line != pj.Line {
    68  		return pi.Line < pj.Line
    69  	}
    70  	if pi.Column != pj.Column {
    71  		return pi.Column < pj.Column
    72  	}
    73  
    74  	return p[i].Text < p[j].Text
    75  }
    76  
    77  // Lint lints src.
    78  func (l *Linter) Lint(filename string, src []byte) ([]Problem, error) {
    79  	return l.LintFiles(map[string][]byte{filename: src})
    80  }
    81  
    82  // LintFiles lints a set of files of a single package.
    83  // The argument is a map of filename to source.
    84  func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) {
    85  	pkg := &pkg{
    86  		fset:  token.NewFileSet(),
    87  		files: make(map[string]*file),
    88  	}
    89  	var pkgName string
    90  	for filename, src := range files {
    91  		if isGenerated(src) {
    92  			continue // See issue #239
    93  		}
    94  		f, err := parser.ParseFile(pkg.fset, filename, src, parser.ParseComments)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		if pkgName == "" {
    99  			pkgName = f.Name.Name
   100  		} else if f.Name.Name != pkgName {
   101  			return nil, fmt.Errorf("%s is in package %s, not %s", filename, f.Name.Name, pkgName)
   102  		}
   103  		pkg.files[filename] = &file{
   104  			pkg:      pkg,
   105  			f:        f,
   106  			fset:     pkg.fset,
   107  			src:      src,
   108  			filename: filename,
   109  		}
   110  	}
   111  	if len(pkg.files) == 0 {
   112  		return nil, nil
   113  	}
   114  	return pkg.lint(), nil
   115  }
   116  
   117  var (
   118  	genHdr = []byte("// Code generated ")
   119  	genFtr = []byte(" DO NOT EDIT.")
   120  )
   121  
   122  // isGenerated reports whether the source file is generated code
   123  // according the rules from https://golang.org/s/generatedcode.
   124  func isGenerated(src []byte) bool {
   125  	sc := bufio.NewScanner(bytes.NewReader(src))
   126  	for sc.Scan() {
   127  		b := sc.Bytes()
   128  		if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
   129  			return true
   130  		}
   131  	}
   132  	return false
   133  }
   134  
   135  // pkg represents a package being linted.
   136  type pkg struct {
   137  	fset  *token.FileSet
   138  	files map[string]*file
   139  
   140  	typesPkg  *types.Package
   141  	typesInfo *types.Info
   142  
   143  	// sortable is the set of types in the package that implement sort.Interface.
   144  	sortable map[string]bool
   145  	// main is whether this is a "main" package.
   146  	main bool
   147  
   148  	problems []Problem
   149  }
   150  
   151  func (p *pkg) lint() []Problem {
   152  	if err := p.typeCheck(); err != nil {
   153  		/* TODO(dsymonds): Consider reporting these errors when golint operates on entire packages.
   154  		if e, ok := err.(types.Error); ok {
   155  			pos := p.fset.Position(e.Pos)
   156  			conf := 1.0
   157  			if strings.Contains(e.Msg, "can't find import: ") {
   158  				// Golint is probably being run in a context that doesn't support
   159  				// typechecking (e.g. package files aren't found), so don't warn about it.
   160  				conf = 0
   161  			}
   162  			if conf > 0 {
   163  				p.errorfAt(pos, conf, category("typechecking"), e.Msg)
   164  			}
   165  
   166  			// TODO(dsymonds): Abort if !e.Soft?
   167  		}
   168  		*/
   169  	}
   170  
   171  	p.scanSortable()
   172  	p.main = p.isMain()
   173  
   174  	for _, f := range p.files {
   175  		f.lint()
   176  	}
   177  
   178  	sort.Sort(byPosition(p.problems))
   179  
   180  	return p.problems
   181  }
   182  
   183  // file represents a file being linted.
   184  type file struct {
   185  	pkg      *pkg
   186  	f        *ast.File
   187  	fset     *token.FileSet
   188  	src      []byte
   189  	filename string
   190  }
   191  
   192  func (f *file) isTest() bool { return strings.HasSuffix(f.filename, "_test.go") }
   193  
   194  func (f *file) lint() {
   195  	f.lintPackageComment()
   196  	f.lintImports()
   197  	f.lintBlankImports()
   198  	f.lintExported()
   199  	f.lintNames()
   200  	f.lintVarDecls()
   201  	f.lintElses()
   202  	f.lintRanges()
   203  	f.lintErrorf()
   204  	f.lintErrors()
   205  	f.lintErrorStrings()
   206  	f.lintReceiverNames()
   207  	f.lintIncDec()
   208  	f.lintErrorReturn()
   209  	f.lintUnexportedReturn()
   210  	f.lintTimeNames()
   211  	f.lintContextKeyTypes()
   212  	f.lintContextArgs()
   213  }
   214  
   215  type link string
   216  type category string
   217  
   218  // The variadic arguments may start with link and category types,
   219  // and must end with a format string and any arguments.
   220  // It returns the new Problem.
   221  func (f *file) errorf(n ast.Node, confidence float64, args ...interface{}) *Problem {
   222  	pos := f.fset.Position(n.Pos())
   223  	if pos.Filename == "" {
   224  		pos.Filename = f.filename
   225  	}
   226  	return f.pkg.errorfAt(pos, confidence, args...)
   227  }
   228  
   229  func (p *pkg) errorfAt(pos token.Position, confidence float64, args ...interface{}) *Problem {
   230  	problem := Problem{
   231  		Position:   pos,
   232  		Confidence: confidence,
   233  	}
   234  	if pos.Filename != "" {
   235  		// The file might not exist in our mapping if a //line directive was encountered.
   236  		if f, ok := p.files[pos.Filename]; ok {
   237  			problem.LineText = srcLine(f.src, pos)
   238  		}
   239  	}
   240  
   241  argLoop:
   242  	for len(args) > 1 { // always leave at least the format string in args
   243  		switch v := args[0].(type) {
   244  		case link:
   245  			problem.Link = string(v)
   246  		case category:
   247  			problem.Category = string(v)
   248  		default:
   249  			break argLoop
   250  		}
   251  		args = args[1:]
   252  	}
   253  
   254  	problem.Text = fmt.Sprintf(args[0].(string), args[1:]...)
   255  
   256  	p.problems = append(p.problems, problem)
   257  	return &p.problems[len(p.problems)-1]
   258  }
   259  
   260  var newImporter = func(fset *token.FileSet) types.ImporterFrom {
   261  	return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
   262  }
   263  
   264  func (p *pkg) typeCheck() error {
   265  	config := &types.Config{
   266  		// By setting a no-op error reporter, the type checker does as much work as possible.
   267  		Error:    func(error) {},
   268  		Importer: newImporter(p.fset),
   269  	}
   270  	info := &types.Info{
   271  		Types:  make(map[ast.Expr]types.TypeAndValue),
   272  		Defs:   make(map[*ast.Ident]types.Object),
   273  		Uses:   make(map[*ast.Ident]types.Object),
   274  		Scopes: make(map[ast.Node]*types.Scope),
   275  	}
   276  	var anyFile *file
   277  	var astFiles []*ast.File
   278  	for _, f := range p.files {
   279  		anyFile = f
   280  		astFiles = append(astFiles, f.f)
   281  	}
   282  	pkg, err := config.Check(anyFile.f.Name.Name, p.fset, astFiles, info)
   283  	// Remember the typechecking info, even if config.Check failed,
   284  	// since we will get partial information.
   285  	p.typesPkg = pkg
   286  	p.typesInfo = info
   287  	return err
   288  }
   289  
   290  func (p *pkg) typeOf(expr ast.Expr) types.Type {
   291  	if p.typesInfo == nil {
   292  		return nil
   293  	}
   294  	return p.typesInfo.TypeOf(expr)
   295  }
   296  
   297  func (p *pkg) isNamedType(typ types.Type, importPath, name string) bool {
   298  	n, ok := typ.(*types.Named)
   299  	if !ok {
   300  		return false
   301  	}
   302  	tn := n.Obj()
   303  	return tn != nil && tn.Pkg() != nil && tn.Pkg().Path() == importPath && tn.Name() == name
   304  }
   305  
   306  // scopeOf returns the tightest scope encompassing id.
   307  func (p *pkg) scopeOf(id *ast.Ident) *types.Scope {
   308  	var scope *types.Scope
   309  	if obj := p.typesInfo.ObjectOf(id); obj != nil {
   310  		scope = obj.Parent()
   311  	}
   312  	if scope == p.typesPkg.Scope() {
   313  		// We were given a top-level identifier.
   314  		// Use the file-level scope instead of the package-level scope.
   315  		pos := id.Pos()
   316  		for _, f := range p.files {
   317  			if f.f.Pos() <= pos && pos < f.f.End() {
   318  				scope = p.typesInfo.Scopes[f.f]
   319  				break
   320  			}
   321  		}
   322  	}
   323  	return scope
   324  }
   325  
   326  func (p *pkg) scanSortable() {
   327  	p.sortable = make(map[string]bool)
   328  
   329  	// bitfield for which methods exist on each type.
   330  	const (
   331  		Len = 1 << iota
   332  		Less
   333  		Swap
   334  	)
   335  	nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap}
   336  	has := make(map[string]int)
   337  	for _, f := range p.files {
   338  		f.walk(func(n ast.Node) bool {
   339  			fn, ok := n.(*ast.FuncDecl)
   340  			if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
   341  				return true
   342  			}
   343  			// TODO(dsymonds): We could check the signature to be more precise.
   344  			recv := receiverType(fn)
   345  			if i, ok := nmap[fn.Name.Name]; ok {
   346  				has[recv] |= i
   347  			}
   348  			return false
   349  		})
   350  	}
   351  	for typ, ms := range has {
   352  		if ms == Len|Less|Swap {
   353  			p.sortable[typ] = true
   354  		}
   355  	}
   356  }
   357  
   358  func (p *pkg) isMain() bool {
   359  	for _, f := range p.files {
   360  		if f.isMain() {
   361  			return true
   362  		}
   363  	}
   364  	return false
   365  }
   366  
   367  func (f *file) isMain() bool {
   368  	if f.f.Name.Name == "main" {
   369  		return true
   370  	}
   371  	return false
   372  }
   373  
   374  // lintPackageComment checks package comments. It complains if
   375  // there is no package comment, or if it is not of the right form.
   376  // This has a notable false positive in that a package comment
   377  // could rightfully appear in a different file of the same package,
   378  // but that's not easy to fix since this linter is file-oriented.
   379  func (f *file) lintPackageComment() {
   380  	if f.isTest() {
   381  		return
   382  	}
   383  
   384  	const ref = styleGuideBase + "#package-comments"
   385  	prefix := "Package " + f.f.Name.Name + " "
   386  
   387  	// Look for a detached package comment.
   388  	// First, scan for the last comment that occurs before the "package" keyword.
   389  	var lastCG *ast.CommentGroup
   390  	for _, cg := range f.f.Comments {
   391  		if cg.Pos() > f.f.Package {
   392  			// Gone past "package" keyword.
   393  			break
   394  		}
   395  		lastCG = cg
   396  	}
   397  	if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) {
   398  		endPos := f.fset.Position(lastCG.End())
   399  		pkgPos := f.fset.Position(f.f.Package)
   400  		if endPos.Line+1 < pkgPos.Line {
   401  			// There isn't a great place to anchor this error;
   402  			// the start of the blank lines between the doc and the package statement
   403  			// is at least pointing at the location of the problem.
   404  			pos := token.Position{
   405  				Filename: endPos.Filename,
   406  				// Offset not set; it is non-trivial, and doesn't appear to be needed.
   407  				Line:   endPos.Line + 1,
   408  				Column: 1,
   409  			}
   410  			f.pkg.errorfAt(pos, 0.9, link(ref), category("comments"), "package comment is detached; there should be no blank lines between it and the package statement")
   411  			return
   412  		}
   413  	}
   414  
   415  	if f.f.Doc == nil {
   416  		f.errorf(f.f, 0.2, link(ref), category("comments"), "should have a package comment, unless it's in another file for this package")
   417  		return
   418  	}
   419  	s := f.f.Doc.Text()
   420  	if ts := strings.TrimLeft(s, " \t"); ts != s {
   421  		f.errorf(f.f.Doc, 1, link(ref), category("comments"), "package comment should not have leading space")
   422  		s = ts
   423  	}
   424  	// Only non-main packages need to keep to this form.
   425  	if !f.pkg.main && !strings.HasPrefix(s, prefix) {
   426  		f.errorf(f.f.Doc, 1, link(ref), category("comments"), `package comment should be of the form "%s..."`, prefix)
   427  	}
   428  }
   429  
   430  // lintBlankImports complains if a non-main package has blank imports that are
   431  // not documented.
   432  func (f *file) lintBlankImports() {
   433  	// In package main and in tests, we don't complain about blank imports.
   434  	if f.pkg.main || f.isTest() {
   435  		return
   436  	}
   437  
   438  	// The first element of each contiguous group of blank imports should have
   439  	// an explanatory comment of some kind.
   440  	for i, imp := range f.f.Imports {
   441  		pos := f.fset.Position(imp.Pos())
   442  
   443  		if !isBlank(imp.Name) {
   444  			continue // Ignore non-blank imports.
   445  		}
   446  		if i > 0 {
   447  			prev := f.f.Imports[i-1]
   448  			prevPos := f.fset.Position(prev.Pos())
   449  			if isBlank(prev.Name) && prevPos.Line+1 == pos.Line {
   450  				continue // A subsequent blank in a group.
   451  			}
   452  		}
   453  
   454  		// This is the first blank import of a group.
   455  		if imp.Doc == nil && imp.Comment == nil {
   456  			ref := ""
   457  			f.errorf(imp, 1, link(ref), category("imports"), "a blank import should be only in a main or test package, or have a comment justifying it")
   458  		}
   459  	}
   460  }
   461  
   462  // lintImports examines import blocks.
   463  func (f *file) lintImports() {
   464  	for i, is := range f.f.Imports {
   465  		_ = i
   466  		if is.Name != nil && is.Name.Name == "." && !f.isTest() {
   467  			f.errorf(is, 1, link(styleGuideBase+"#import-dot"), category("imports"), "should not use dot imports")
   468  		}
   469  
   470  	}
   471  }
   472  
   473  const docCommentsLink = styleGuideBase + "#doc-comments"
   474  
   475  // lintExported examines the exported names.
   476  // It complains if any required doc comments are missing,
   477  // or if they are not of the right form. The exact rules are in
   478  // lintFuncDoc, lintTypeDoc and lintValueSpecDoc; this function
   479  // also tracks the GenDecl structure being traversed to permit
   480  // doc comments for constants to be on top of the const block.
   481  // It also complains if the names stutter when combined with
   482  // the package name.
   483  func (f *file) lintExported() {
   484  	if f.isTest() {
   485  		return
   486  	}
   487  
   488  	var lastGen *ast.GenDecl // last GenDecl entered.
   489  
   490  	// Set of GenDecls that have already had missing comments flagged.
   491  	genDeclMissingComments := make(map[*ast.GenDecl]bool)
   492  
   493  	f.walk(func(node ast.Node) bool {
   494  		switch v := node.(type) {
   495  		case *ast.GenDecl:
   496  			if v.Tok == token.IMPORT {
   497  				return false
   498  			}
   499  			// token.CONST, token.TYPE or token.VAR
   500  			lastGen = v
   501  			return true
   502  		case *ast.FuncDecl:
   503  			f.lintFuncDoc(v)
   504  			if v.Recv == nil {
   505  				// Only check for stutter on functions, not methods.
   506  				// Method names are not used package-qualified.
   507  				f.checkStutter(v.Name, "func")
   508  			}
   509  			// Don't proceed inside funcs.
   510  			return false
   511  		case *ast.TypeSpec:
   512  			// inside a GenDecl, which usually has the doc
   513  			doc := v.Doc
   514  			if doc == nil {
   515  				doc = lastGen.Doc
   516  			}
   517  			f.lintTypeDoc(v, doc)
   518  			f.checkStutter(v.Name, "type")
   519  			// Don't proceed inside types.
   520  			return false
   521  		case *ast.ValueSpec:
   522  			f.lintValueSpecDoc(v, lastGen, genDeclMissingComments)
   523  			return false
   524  		}
   525  		return true
   526  	})
   527  }
   528  
   529  var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
   530  
   531  // knownNameExceptions is a set of names that are known to be exempt from naming checks.
   532  // This is usually because they are constrained by having to match names in the
   533  // standard library.
   534  var knownNameExceptions = map[string]bool{
   535  	"LastInsertId": true, // must match database/sql
   536  	"kWh":          true,
   537  }
   538  
   539  // lintNames examines all names in the file.
   540  // It complains if any use underscores or incorrect known initialisms.
   541  func (f *file) lintNames() {
   542  	// Package names need slightly different handling than other names.
   543  	if strings.Contains(f.f.Name.Name, "_") && !strings.HasSuffix(f.f.Name.Name, "_test") {
   544  		f.errorf(f.f, 1, link("http://golang.org/doc/effective_go.html#package-names"), category("naming"), "don't use an underscore in package name")
   545  	}
   546  
   547  	check := func(id *ast.Ident, thing string) {
   548  		if id.Name == "_" {
   549  			return
   550  		}
   551  		if knownNameExceptions[id.Name] {
   552  			return
   553  		}
   554  
   555  		// Handle two common styles from other languages that don't belong in Go.
   556  		if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") {
   557  			f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use ALL_CAPS in Go names; use CamelCase")
   558  			return
   559  		}
   560  		if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
   561  			should := string(id.Name[1]+'a'-'A') + id.Name[2:]
   562  			f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use leading k in Go names; %s %s should be %s", thing, id.Name, should)
   563  		}
   564  
   565  		should := lintName(id.Name)
   566  		if id.Name == should {
   567  			return
   568  		}
   569  
   570  		if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") {
   571  			f.errorf(id, 0.9, link("http://golang.org/doc/effective_go.html#mixed-caps"), category("naming"), "don't use underscores in Go names; %s %s should be %s", thing, id.Name, should)
   572  			return
   573  		}
   574  		f.errorf(id, 0.8, link(styleGuideBase+"#initialisms"), category("naming"), "%s %s should be %s", thing, id.Name, should)
   575  	}
   576  	checkList := func(fl *ast.FieldList, thing string) {
   577  		if fl == nil {
   578  			return
   579  		}
   580  		for _, f := range fl.List {
   581  			for _, id := range f.Names {
   582  				check(id, thing)
   583  			}
   584  		}
   585  	}
   586  	f.walk(func(node ast.Node) bool {
   587  		switch v := node.(type) {
   588  		case *ast.AssignStmt:
   589  			if v.Tok == token.ASSIGN {
   590  				return true
   591  			}
   592  			for _, exp := range v.Lhs {
   593  				if id, ok := exp.(*ast.Ident); ok {
   594  					check(id, "var")
   595  				}
   596  			}
   597  		case *ast.FuncDecl:
   598  			if f.isTest() && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
   599  				return true
   600  			}
   601  
   602  			thing := "func"
   603  			if v.Recv != nil {
   604  				thing = "method"
   605  			}
   606  
   607  			// Exclude naming warnings for functions that are exported to C but
   608  			// not exported in the Go API.
   609  			// See https://github.com/golang/lint/issues/144.
   610  			if ast.IsExported(v.Name.Name) || !isCgoExported(v) {
   611  				check(v.Name, thing)
   612  			}
   613  
   614  			checkList(v.Type.Params, thing+" parameter")
   615  			checkList(v.Type.Results, thing+" result")
   616  		case *ast.GenDecl:
   617  			if v.Tok == token.IMPORT {
   618  				return true
   619  			}
   620  			var thing string
   621  			switch v.Tok {
   622  			case token.CONST:
   623  				thing = "const"
   624  			case token.TYPE:
   625  				thing = "type"
   626  			case token.VAR:
   627  				thing = "var"
   628  			}
   629  			for _, spec := range v.Specs {
   630  				switch s := spec.(type) {
   631  				case *ast.TypeSpec:
   632  					check(s.Name, thing)
   633  				case *ast.ValueSpec:
   634  					for _, id := range s.Names {
   635  						check(id, thing)
   636  					}
   637  				}
   638  			}
   639  		case *ast.InterfaceType:
   640  			// Do not check interface method names.
   641  			// They are often constrainted by the method names of concrete types.
   642  			for _, x := range v.Methods.List {
   643  				ft, ok := x.Type.(*ast.FuncType)
   644  				if !ok { // might be an embedded interface name
   645  					continue
   646  				}
   647  				checkList(ft.Params, "interface method parameter")
   648  				checkList(ft.Results, "interface method result")
   649  			}
   650  		case *ast.RangeStmt:
   651  			if v.Tok == token.ASSIGN {
   652  				return true
   653  			}
   654  			if id, ok := v.Key.(*ast.Ident); ok {
   655  				check(id, "range var")
   656  			}
   657  			if id, ok := v.Value.(*ast.Ident); ok {
   658  				check(id, "range var")
   659  			}
   660  		case *ast.StructType:
   661  			for _, f := range v.Fields.List {
   662  				for _, id := range f.Names {
   663  					check(id, "struct field")
   664  				}
   665  			}
   666  		}
   667  		return true
   668  	})
   669  }
   670  
   671  // lintName returns a different name if it should be different.
   672  func lintName(name string) (should string) {
   673  	// Fast path for simple cases: "_" and all lowercase.
   674  	if name == "_" {
   675  		return name
   676  	}
   677  	allLower := true
   678  	for _, r := range name {
   679  		if !unicode.IsLower(r) {
   680  			allLower = false
   681  			break
   682  		}
   683  	}
   684  	if allLower {
   685  		return name
   686  	}
   687  
   688  	// Split camelCase at any lower->upper transition, and split on underscores.
   689  	// Check each word for common initialisms.
   690  	runes := []rune(name)
   691  	w, i := 0, 0 // index of start of word, scan
   692  	for i+1 <= len(runes) {
   693  		eow := false // whether we hit the end of a word
   694  		if i+1 == len(runes) {
   695  			eow = true
   696  		} else if runes[i+1] == '_' {
   697  			// underscore; shift the remainder forward over any run of underscores
   698  			eow = true
   699  			n := 1
   700  			for i+n+1 < len(runes) && runes[i+n+1] == '_' {
   701  				n++
   702  			}
   703  
   704  			// Leave at most one underscore if the underscore is between two digits
   705  			if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
   706  				n--
   707  			}
   708  
   709  			copy(runes[i+1:], runes[i+n+1:])
   710  			runes = runes[:len(runes)-n]
   711  		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
   712  			// lower->non-lower
   713  			eow = true
   714  		}
   715  		i++
   716  		if !eow {
   717  			continue
   718  		}
   719  
   720  		// [w,i) is a word.
   721  		word := string(runes[w:i])
   722  		if u := strings.ToUpper(word); commonInitialisms[u] {
   723  			// Keep consistent case, which is lowercase only at the start.
   724  			if w == 0 && unicode.IsLower(runes[w]) {
   725  				u = strings.ToLower(u)
   726  			}
   727  			// All the common initialisms are ASCII,
   728  			// so we can replace the bytes exactly.
   729  			copy(runes[w:], []rune(u))
   730  		} else if w > 0 && strings.ToLower(word) == word {
   731  			// already all lowercase, and not the first word, so uppercase the first character.
   732  			runes[w] = unicode.ToUpper(runes[w])
   733  		}
   734  		w = i
   735  	}
   736  	return string(runes)
   737  }
   738  
   739  // commonInitialisms is a set of common initialisms.
   740  // Only add entries that are highly unlikely to be non-initialisms.
   741  // For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
   742  var commonInitialisms = map[string]bool{
   743  	"ACL":   true,
   744  	"API":   true,
   745  	"ASCII": true,
   746  	"CPU":   true,
   747  	"CSS":   true,
   748  	"DNS":   true,
   749  	"EOF":   true,
   750  	"GUID":  true,
   751  	"HTML":  true,
   752  	"HTTP":  true,
   753  	"HTTPS": true,
   754  	"ID":    true,
   755  	"IP":    true,
   756  	"JSON":  true,
   757  	"LHS":   true,
   758  	"QPS":   true,
   759  	"RAM":   true,
   760  	"RHS":   true,
   761  	"RPC":   true,
   762  	"SLA":   true,
   763  	"SMTP":  true,
   764  	"SQL":   true,
   765  	"SSH":   true,
   766  	"TCP":   true,
   767  	"TLS":   true,
   768  	"TTL":   true,
   769  	"UDP":   true,
   770  	"UI":    true,
   771  	"UID":   true,
   772  	"UUID":  true,
   773  	"URI":   true,
   774  	"URL":   true,
   775  	"UTF8":  true,
   776  	"VM":    true,
   777  	"XML":   true,
   778  	"XMPP":  true,
   779  	"XSRF":  true,
   780  	"XSS":   true,
   781  }
   782  
   783  // lintTypeDoc examines the doc comment on a type.
   784  // It complains if they are missing from an exported type,
   785  // or if they are not of the standard form.
   786  func (f *file) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) {
   787  	if !ast.IsExported(t.Name.Name) {
   788  		return
   789  	}
   790  	if doc == nil {
   791  		f.errorf(t, 1, link(docCommentsLink), category("comments"), "exported type %v should have comment or be unexported", t.Name)
   792  		return
   793  	}
   794  
   795  	s := doc.Text()
   796  	articles := [...]string{"A", "An", "The"}
   797  	for _, a := range articles {
   798  		if strings.HasPrefix(s, a+" ") {
   799  			s = s[len(a)+1:]
   800  			break
   801  		}
   802  	}
   803  	if !strings.HasPrefix(s, t.Name.Name+" ") {
   804  		f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported type %v should be of the form "%v ..." (with optional leading article)`, t.Name, t.Name)
   805  	}
   806  }
   807  
   808  var commonMethods = map[string]bool{
   809  	"Error":     true,
   810  	"Read":      true,
   811  	"ServeHTTP": true,
   812  	"String":    true,
   813  	"Write":     true,
   814  }
   815  
   816  // lintFuncDoc examines doc comments on functions and methods.
   817  // It complains if they are missing, or not of the right form.
   818  // It has specific exclusions for well-known methods (see commonMethods above).
   819  func (f *file) lintFuncDoc(fn *ast.FuncDecl) {
   820  	if !ast.IsExported(fn.Name.Name) {
   821  		// func is unexported
   822  		return
   823  	}
   824  	kind := "function"
   825  	name := fn.Name.Name
   826  	if fn.Recv != nil && len(fn.Recv.List) > 0 {
   827  		// method
   828  		kind = "method"
   829  		recv := receiverType(fn)
   830  		if !ast.IsExported(recv) {
   831  			// receiver is unexported
   832  			return
   833  		}
   834  		if commonMethods[name] {
   835  			return
   836  		}
   837  		switch name {
   838  		case "Len", "Less", "Swap":
   839  			if f.pkg.sortable[recv] {
   840  				return
   841  			}
   842  		}
   843  		name = recv + "." + name
   844  	}
   845  	if fn.Doc == nil {
   846  		f.errorf(fn, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment or be unexported", kind, name)
   847  		return
   848  	}
   849  	s := fn.Doc.Text()
   850  	prefix := fn.Name.Name + " "
   851  	if !strings.HasPrefix(s, prefix) {
   852  		f.errorf(fn.Doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
   853  	}
   854  }
   855  
   856  // lintValueSpecDoc examines package-global variables and constants.
   857  // It complains if they are not individually declared,
   858  // or if they are not suitably documented in the right form (unless they are in a block that is commented).
   859  func (f *file) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) {
   860  	kind := "var"
   861  	if gd.Tok == token.CONST {
   862  		kind = "const"
   863  	}
   864  
   865  	if len(vs.Names) > 1 {
   866  		// Check that none are exported except for the first.
   867  		for _, n := range vs.Names[1:] {
   868  			if ast.IsExported(n.Name) {
   869  				f.errorf(vs, 1, category("comments"), "exported %s %s should have its own declaration", kind, n.Name)
   870  				return
   871  			}
   872  		}
   873  	}
   874  
   875  	// Only one name.
   876  	name := vs.Names[0].Name
   877  	if !ast.IsExported(name) {
   878  		return
   879  	}
   880  
   881  	if vs.Doc == nil && gd.Doc == nil {
   882  		if genDeclMissingComments[gd] {
   883  			return
   884  		}
   885  		block := ""
   886  		if kind == "const" && gd.Lparen.IsValid() {
   887  			block = " (or a comment on this block)"
   888  		}
   889  		f.errorf(vs, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment%s or be unexported", kind, name, block)
   890  		genDeclMissingComments[gd] = true
   891  		return
   892  	}
   893  	// If this GenDecl has parens and a comment, we don't check its comment form.
   894  	if gd.Lparen.IsValid() && gd.Doc != nil {
   895  		return
   896  	}
   897  	// The relevant text to check will be on either vs.Doc or gd.Doc.
   898  	// Use vs.Doc preferentially.
   899  	doc := vs.Doc
   900  	if doc == nil {
   901  		doc = gd.Doc
   902  	}
   903  	prefix := name + " "
   904  	if !strings.HasPrefix(doc.Text(), prefix) {
   905  		f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
   906  	}
   907  }
   908  
   909  func (f *file) checkStutter(id *ast.Ident, thing string) {
   910  	pkg, name := f.f.Name.Name, id.Name
   911  	if !ast.IsExported(name) {
   912  		// unexported name
   913  		return
   914  	}
   915  	// A name stutters if the package name is a strict prefix
   916  	// and the next character of the name starts a new word.
   917  	if len(name) <= len(pkg) {
   918  		// name is too short to stutter.
   919  		// This permits the name to be the same as the package name.
   920  		return
   921  	}
   922  	if !strings.EqualFold(pkg, name[:len(pkg)]) {
   923  		return
   924  	}
   925  	// We can assume the name is well-formed UTF-8.
   926  	// If the next rune after the package name is uppercase or an underscore
   927  	// the it's starting a new word and thus this name stutters.
   928  	rem := name[len(pkg):]
   929  	if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) {
   930  		f.errorf(id, 0.8, link(styleGuideBase+"#package-names"), category("naming"), "%s name will be used as %s.%s by other packages, and that stutters; consider calling this %s", thing, pkg, name, rem)
   931  	}
   932  }
   933  
   934  // zeroLiteral is a set of ast.BasicLit values that are zero values.
   935  // It is not exhaustive.
   936  var zeroLiteral = map[string]bool{
   937  	"false": true, // bool
   938  	// runes
   939  	`'\x00'`: true,
   940  	`'\000'`: true,
   941  	// strings
   942  	`""`: true,
   943  	"``": true,
   944  	// numerics
   945  	"0":   true,
   946  	"0.":  true,
   947  	"0.0": true,
   948  	"0i":  true,
   949  }
   950  
   951  // lintVarDecls examines variable declarations. It complains about declarations with
   952  // redundant LHS types that can be inferred from the RHS.
   953  func (f *file) lintVarDecls() {
   954  	var lastGen *ast.GenDecl // last GenDecl entered.
   955  
   956  	f.walk(func(node ast.Node) bool {
   957  		switch v := node.(type) {
   958  		case *ast.GenDecl:
   959  			if v.Tok != token.CONST && v.Tok != token.VAR {
   960  				return false
   961  			}
   962  			lastGen = v
   963  			return true
   964  		case *ast.ValueSpec:
   965  			if lastGen.Tok == token.CONST {
   966  				return false
   967  			}
   968  			if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 {
   969  				return false
   970  			}
   971  			rhs := v.Values[0]
   972  			// An underscore var appears in a common idiom for compile-time interface satisfaction,
   973  			// as in "var _ Interface = (*Concrete)(nil)".
   974  			if isIdent(v.Names[0], "_") {
   975  				return false
   976  			}
   977  			// If the RHS is a zero value, suggest dropping it.
   978  			zero := false
   979  			if lit, ok := rhs.(*ast.BasicLit); ok {
   980  				zero = zeroLiteral[lit.Value]
   981  			} else if isIdent(rhs, "nil") {
   982  				zero = true
   983  			}
   984  			if zero {
   985  				f.errorf(rhs, 0.9, category("zero-value"), "should drop = %s from declaration of var %s; it is the zero value", f.render(rhs), v.Names[0])
   986  				return false
   987  			}
   988  			lhsTyp := f.pkg.typeOf(v.Type)
   989  			rhsTyp := f.pkg.typeOf(rhs)
   990  
   991  			if !validType(lhsTyp) || !validType(rhsTyp) {
   992  				// Type checking failed (often due to missing imports).
   993  				return false
   994  			}
   995  
   996  			if !types.Identical(lhsTyp, rhsTyp) {
   997  				// Assignment to a different type is not redundant.
   998  				return false
   999  			}
  1000  
  1001  			// The next three conditions are for suppressing the warning in situations
  1002  			// where we were unable to typecheck.
  1003  
  1004  			// If the LHS type is an interface, don't warn, since it is probably a
  1005  			// concrete type on the RHS. Note that our feeble lexical check here
  1006  			// will only pick up interface{} and other literal interface types;
  1007  			// that covers most of the cases we care to exclude right now.
  1008  			if _, ok := v.Type.(*ast.InterfaceType); ok {
  1009  				return false
  1010  			}
  1011  			// If the RHS is an untyped const, only warn if the LHS type is its default type.
  1012  			if defType, ok := f.isUntypedConst(rhs); ok && !isIdent(v.Type, defType) {
  1013  				return false
  1014  			}
  1015  
  1016  			f.errorf(v.Type, 0.8, category("type-inference"), "should omit type %s from declaration of var %s; it will be inferred from the right-hand side", f.render(v.Type), v.Names[0])
  1017  			return false
  1018  		}
  1019  		return true
  1020  	})
  1021  }
  1022  
  1023  func validType(T types.Type) bool {
  1024  	return T != nil &&
  1025  		T != types.Typ[types.Invalid] &&
  1026  		!strings.Contains(T.String(), "invalid type") // good but not foolproof
  1027  }
  1028  
  1029  // lintElses examines else blocks. It complains about any else block whose if block ends in a return.
  1030  func (f *file) lintElses() {
  1031  	// We don't want to flag if { } else if { } else { } constructions.
  1032  	// They will appear as an IfStmt whose Else field is also an IfStmt.
  1033  	// Record such a node so we ignore it when we visit it.
  1034  	ignore := make(map[*ast.IfStmt]bool)
  1035  
  1036  	f.walk(func(node ast.Node) bool {
  1037  		ifStmt, ok := node.(*ast.IfStmt)
  1038  		if !ok || ifStmt.Else == nil {
  1039  			return true
  1040  		}
  1041  		if ignore[ifStmt] {
  1042  			return true
  1043  		}
  1044  		if elseif, ok := ifStmt.Else.(*ast.IfStmt); ok {
  1045  			ignore[elseif] = true
  1046  			return true
  1047  		}
  1048  		if _, ok := ifStmt.Else.(*ast.BlockStmt); !ok {
  1049  			// only care about elses without conditions
  1050  			return true
  1051  		}
  1052  		if len(ifStmt.Body.List) == 0 {
  1053  			return true
  1054  		}
  1055  		shortDecl := false // does the if statement have a ":=" initialization statement?
  1056  		if ifStmt.Init != nil {
  1057  			if as, ok := ifStmt.Init.(*ast.AssignStmt); ok && as.Tok == token.DEFINE {
  1058  				shortDecl = true
  1059  			}
  1060  		}
  1061  		lastStmt := ifStmt.Body.List[len(ifStmt.Body.List)-1]
  1062  		if _, ok := lastStmt.(*ast.ReturnStmt); ok {
  1063  			extra := ""
  1064  			if shortDecl {
  1065  				extra = " (move short variable declaration to its own line if necessary)"
  1066  			}
  1067  			f.errorf(ifStmt.Else, 1, link(styleGuideBase+"#indent-error-flow"), category("indent"), "if block ends with a return statement, so drop this else and outdent its block"+extra)
  1068  		}
  1069  		return true
  1070  	})
  1071  }
  1072  
  1073  // lintRanges examines range clauses. It complains about redundant constructions.
  1074  func (f *file) lintRanges() {
  1075  	f.walk(func(node ast.Node) bool {
  1076  		rs, ok := node.(*ast.RangeStmt)
  1077  		if !ok {
  1078  			return true
  1079  		}
  1080  		if rs.Value == nil {
  1081  			// for x = range m { ... }
  1082  			return true // single var form
  1083  		}
  1084  		if !isIdent(rs.Value, "_") {
  1085  			// for ?, y = range m { ... }
  1086  			return true
  1087  		}
  1088  
  1089  		p := f.errorf(rs.Value, 1, category("range-loop"), "should omit 2nd value from range; this loop is equivalent to `for %s %s range ...`", f.render(rs.Key), rs.Tok)
  1090  
  1091  		newRS := *rs // shallow copy
  1092  		newRS.Value = nil
  1093  		p.ReplacementLine = f.firstLineOf(&newRS, rs)
  1094  
  1095  		return true
  1096  	})
  1097  }
  1098  
  1099  // lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation.
  1100  func (f *file) lintErrorf() {
  1101  	f.walk(func(node ast.Node) bool {
  1102  		ce, ok := node.(*ast.CallExpr)
  1103  		if !ok || len(ce.Args) != 1 {
  1104  			return true
  1105  		}
  1106  		isErrorsNew := isPkgDot(ce.Fun, "errors", "New")
  1107  		var isTestingError bool
  1108  		se, ok := ce.Fun.(*ast.SelectorExpr)
  1109  		if ok && se.Sel.Name == "Error" {
  1110  			if typ := f.pkg.typeOf(se.X); typ != nil {
  1111  				isTestingError = typ.String() == "*testing.T"
  1112  			}
  1113  		}
  1114  		if !isErrorsNew && !isTestingError {
  1115  			return true
  1116  		}
  1117  		arg := ce.Args[0]
  1118  		ce, ok = arg.(*ast.CallExpr)
  1119  		if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
  1120  			return true
  1121  		}
  1122  		errorfPrefix := "fmt"
  1123  		if isTestingError {
  1124  			errorfPrefix = f.render(se.X)
  1125  		}
  1126  		p := f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix)
  1127  
  1128  		m := f.srcLineWithMatch(ce, `^(.*)`+f.render(se)+`\(fmt\.Sprintf\((.*)\)\)(.*)$`)
  1129  		if m != nil {
  1130  			p.ReplacementLine = m[1] + errorfPrefix + ".Errorf(" + m[2] + ")" + m[3]
  1131  		}
  1132  
  1133  		return true
  1134  	})
  1135  }
  1136  
  1137  // lintErrors examines global error vars. It complains if they aren't named in the standard way.
  1138  func (f *file) lintErrors() {
  1139  	for _, decl := range f.f.Decls {
  1140  		gd, ok := decl.(*ast.GenDecl)
  1141  		if !ok || gd.Tok != token.VAR {
  1142  			continue
  1143  		}
  1144  		for _, spec := range gd.Specs {
  1145  			spec := spec.(*ast.ValueSpec)
  1146  			if len(spec.Names) != 1 || len(spec.Values) != 1 {
  1147  				continue
  1148  			}
  1149  			ce, ok := spec.Values[0].(*ast.CallExpr)
  1150  			if !ok {
  1151  				continue
  1152  			}
  1153  			if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
  1154  				continue
  1155  			}
  1156  
  1157  			id := spec.Names[0]
  1158  			prefix := "err"
  1159  			if id.IsExported() {
  1160  				prefix = "Err"
  1161  			}
  1162  			if !strings.HasPrefix(id.Name, prefix) {
  1163  				f.errorf(id, 0.9, category("naming"), "error var %s should have name of the form %sFoo", id.Name, prefix)
  1164  			}
  1165  		}
  1166  	}
  1167  }
  1168  
  1169  func lintErrorString(s string) (isClean bool, conf float64) {
  1170  	const basicConfidence = 0.8
  1171  	const capConfidence = basicConfidence - 0.2
  1172  	first, firstN := utf8.DecodeRuneInString(s)
  1173  	last, _ := utf8.DecodeLastRuneInString(s)
  1174  	if last == '.' || last == ':' || last == '!' || last == '\n' {
  1175  		return false, basicConfidence
  1176  	}
  1177  	if unicode.IsUpper(first) {
  1178  		// People use proper nouns and exported Go identifiers in error strings,
  1179  		// so decrease the confidence of warnings for capitalization.
  1180  		if len(s) <= firstN {
  1181  			return false, capConfidence
  1182  		}
  1183  		// Flag strings starting with something that doesn't look like an initialism.
  1184  		if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) {
  1185  			return false, capConfidence
  1186  		}
  1187  	}
  1188  	return true, 0
  1189  }
  1190  
  1191  // lintErrorStrings examines error strings.
  1192  // It complains if they are capitalized or end in punctuation or a newline.
  1193  func (f *file) lintErrorStrings() {
  1194  	f.walk(func(node ast.Node) bool {
  1195  		ce, ok := node.(*ast.CallExpr)
  1196  		if !ok {
  1197  			return true
  1198  		}
  1199  		if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
  1200  			return true
  1201  		}
  1202  		if len(ce.Args) < 1 {
  1203  			return true
  1204  		}
  1205  		str, ok := ce.Args[0].(*ast.BasicLit)
  1206  		if !ok || str.Kind != token.STRING {
  1207  			return true
  1208  		}
  1209  		s, _ := strconv.Unquote(str.Value) // can assume well-formed Go
  1210  		if s == "" {
  1211  			return true
  1212  		}
  1213  		clean, conf := lintErrorString(s)
  1214  		if clean {
  1215  			return true
  1216  		}
  1217  
  1218  		f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"),
  1219  			"error strings should not be capitalized or end with punctuation or a newline")
  1220  		return true
  1221  	})
  1222  }
  1223  
  1224  // lintReceiverNames examines receiver names. It complains about inconsistent
  1225  // names used for the same type and names such as "this".
  1226  func (f *file) lintReceiverNames() {
  1227  	typeReceiver := map[string]string{}
  1228  	f.walk(func(n ast.Node) bool {
  1229  		fn, ok := n.(*ast.FuncDecl)
  1230  		if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
  1231  			return true
  1232  		}
  1233  		names := fn.Recv.List[0].Names
  1234  		if len(names) < 1 {
  1235  			return true
  1236  		}
  1237  		name := names[0].Name
  1238  		const ref = styleGuideBase + "#receiver-names"
  1239  		if name == "_" {
  1240  			f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore`)
  1241  			return true
  1242  		}
  1243  		if name == "this" || name == "self" {
  1244  			f.errorf(n, 1, link(ref), category("naming"), `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`)
  1245  			return true
  1246  		}
  1247  		recv := receiverType(fn)
  1248  		if prev, ok := typeReceiver[recv]; ok && prev != name {
  1249  			f.errorf(n, 1, link(ref), category("naming"), "receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv)
  1250  			return true
  1251  		}
  1252  		typeReceiver[recv] = name
  1253  		return true
  1254  	})
  1255  }
  1256  
  1257  // lintIncDec examines statements that increment or decrement a variable.
  1258  // It complains if they don't use x++ or x--.
  1259  func (f *file) lintIncDec() {
  1260  	f.walk(func(n ast.Node) bool {
  1261  		as, ok := n.(*ast.AssignStmt)
  1262  		if !ok {
  1263  			return true
  1264  		}
  1265  		if len(as.Lhs) != 1 {
  1266  			return true
  1267  		}
  1268  		if !isOne(as.Rhs[0]) {
  1269  			return true
  1270  		}
  1271  		var suffix string
  1272  		switch as.Tok {
  1273  		case token.ADD_ASSIGN:
  1274  			suffix = "++"
  1275  		case token.SUB_ASSIGN:
  1276  			suffix = "--"
  1277  		default:
  1278  			return true
  1279  		}
  1280  		f.errorf(as, 0.8, category("unary-op"), "should replace %s with %s%s", f.render(as), f.render(as.Lhs[0]), suffix)
  1281  		return true
  1282  	})
  1283  }
  1284  
  1285  // lintErrorReturn examines function declarations that return an error.
  1286  // It complains if the error isn't the last parameter.
  1287  func (f *file) lintErrorReturn() {
  1288  	f.walk(func(n ast.Node) bool {
  1289  		fn, ok := n.(*ast.FuncDecl)
  1290  		if !ok || fn.Type.Results == nil {
  1291  			return true
  1292  		}
  1293  		ret := fn.Type.Results.List
  1294  		if len(ret) <= 1 {
  1295  			return true
  1296  		}
  1297  		// An error return parameter should be the last parameter.
  1298  		// Flag any error parameters found before the last.
  1299  		for _, r := range ret[:len(ret)-1] {
  1300  			if isIdent(r.Type, "error") {
  1301  				f.errorf(fn, 0.9, category("arg-order"), "error should be the last type when returning multiple items")
  1302  				break // only flag one
  1303  			}
  1304  		}
  1305  		return true
  1306  	})
  1307  }
  1308  
  1309  // lintUnexportedReturn examines exported function declarations.
  1310  // It complains if any return an unexported type.
  1311  func (f *file) lintUnexportedReturn() {
  1312  	f.walk(func(n ast.Node) bool {
  1313  		fn, ok := n.(*ast.FuncDecl)
  1314  		if !ok {
  1315  			return true
  1316  		}
  1317  		if fn.Type.Results == nil {
  1318  			return false
  1319  		}
  1320  		if !fn.Name.IsExported() {
  1321  			return false
  1322  		}
  1323  		thing := "func"
  1324  		if fn.Recv != nil && len(fn.Recv.List) > 0 {
  1325  			thing = "method"
  1326  			if !ast.IsExported(receiverType(fn)) {
  1327  				// Don't report exported methods of unexported types,
  1328  				// such as private implementations of sort.Interface.
  1329  				return false
  1330  			}
  1331  		}
  1332  		for _, ret := range fn.Type.Results.List {
  1333  			typ := f.pkg.typeOf(ret.Type)
  1334  			if exportedType(typ) {
  1335  				continue
  1336  			}
  1337  			f.errorf(ret.Type, 0.8, category("unexported-type-in-api"),
  1338  				"exported %s %s returns unexported type %s, which can be annoying to use",
  1339  				thing, fn.Name.Name, typ)
  1340  			break // only flag one
  1341  		}
  1342  		return false
  1343  	})
  1344  }
  1345  
  1346  // exportedType reports whether typ is an exported type.
  1347  // It is imprecise, and will err on the side of returning true,
  1348  // such as for composite types.
  1349  func exportedType(typ types.Type) bool {
  1350  	switch T := typ.(type) {
  1351  	case *types.Named:
  1352  		// Builtin types have no package.
  1353  		return T.Obj().Pkg() == nil || T.Obj().Exported()
  1354  	case *types.Map:
  1355  		return exportedType(T.Key()) && exportedType(T.Elem())
  1356  	case interface {
  1357  		Elem() types.Type
  1358  	}: // array, slice, pointer, chan
  1359  		return exportedType(T.Elem())
  1360  	}
  1361  	// Be conservative about other types, such as struct, interface, etc.
  1362  	return true
  1363  }
  1364  
  1365  // timeSuffixes is a list of name suffixes that imply a time unit.
  1366  // This is not an exhaustive list.
  1367  var timeSuffixes = []string{
  1368  	"Sec", "Secs", "Seconds",
  1369  	"Msec", "Msecs",
  1370  	"Milli", "Millis", "Milliseconds",
  1371  	"Usec", "Usecs", "Microseconds",
  1372  	"MS", "Ms",
  1373  }
  1374  
  1375  func (f *file) lintTimeNames() {
  1376  	f.walk(func(node ast.Node) bool {
  1377  		v, ok := node.(*ast.ValueSpec)
  1378  		if !ok {
  1379  			return true
  1380  		}
  1381  		for _, name := range v.Names {
  1382  			origTyp := f.pkg.typeOf(name)
  1383  			// Look for time.Duration or *time.Duration;
  1384  			// the latter is common when using flag.Duration.
  1385  			typ := origTyp
  1386  			if pt, ok := typ.(*types.Pointer); ok {
  1387  				typ = pt.Elem()
  1388  			}
  1389  			if !f.pkg.isNamedType(typ, "time", "Duration") {
  1390  				continue
  1391  			}
  1392  			suffix := ""
  1393  			for _, suf := range timeSuffixes {
  1394  				if strings.HasSuffix(name.Name, suf) {
  1395  					suffix = suf
  1396  					break
  1397  				}
  1398  			}
  1399  			if suffix == "" {
  1400  				continue
  1401  			}
  1402  			f.errorf(v, 0.9, category("time"), "var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix)
  1403  		}
  1404  		return true
  1405  	})
  1406  }
  1407  
  1408  // lintContextKeyTypes checks for call expressions to context.WithValue with
  1409  // basic types used for the key argument.
  1410  // See: https://golang.org/issue/17293
  1411  func (f *file) lintContextKeyTypes() {
  1412  	f.walk(func(node ast.Node) bool {
  1413  		switch node := node.(type) {
  1414  		case *ast.CallExpr:
  1415  			f.checkContextKeyType(node)
  1416  		}
  1417  
  1418  		return true
  1419  	})
  1420  }
  1421  
  1422  // checkContextKeyType reports an error if the call expression calls
  1423  // context.WithValue with a key argument of basic type.
  1424  func (f *file) checkContextKeyType(x *ast.CallExpr) {
  1425  	sel, ok := x.Fun.(*ast.SelectorExpr)
  1426  	if !ok {
  1427  		return
  1428  	}
  1429  	pkg, ok := sel.X.(*ast.Ident)
  1430  	if !ok || pkg.Name != "context" {
  1431  		return
  1432  	}
  1433  	if sel.Sel.Name != "WithValue" {
  1434  		return
  1435  	}
  1436  
  1437  	// key is second argument to context.WithValue
  1438  	if len(x.Args) != 3 {
  1439  		return
  1440  	}
  1441  	key := f.pkg.typesInfo.Types[x.Args[1]]
  1442  
  1443  	if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid {
  1444  		f.errorf(x, 1.0, category("context"), fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type))
  1445  	}
  1446  }
  1447  
  1448  // lintContextArgs examines function declarations that contain an
  1449  // argument with a type of context.Context
  1450  // It complains if that argument isn't the first parameter.
  1451  func (f *file) lintContextArgs() {
  1452  	f.walk(func(n ast.Node) bool {
  1453  		fn, ok := n.(*ast.FuncDecl)
  1454  		if !ok || len(fn.Type.Params.List) <= 1 {
  1455  			return true
  1456  		}
  1457  		// A context.Context should be the first parameter of a function.
  1458  		// Flag any that show up after the first.
  1459  		for _, arg := range fn.Type.Params.List[1:] {
  1460  			if isPkgDot(arg.Type, "context", "Context") {
  1461  				f.errorf(fn, 0.9, link("https://golang.org/pkg/context/"), category("arg-order"), "context.Context should be the first parameter of a function")
  1462  				break // only flag one
  1463  			}
  1464  		}
  1465  		return true
  1466  	})
  1467  }
  1468  
  1469  // receiverType returns the named type of the method receiver, sans "*",
  1470  // or "invalid-type" if fn.Recv is ill formed.
  1471  func receiverType(fn *ast.FuncDecl) string {
  1472  	switch e := fn.Recv.List[0].Type.(type) {
  1473  	case *ast.Ident:
  1474  		return e.Name
  1475  	case *ast.StarExpr:
  1476  		if id, ok := e.X.(*ast.Ident); ok {
  1477  			return id.Name
  1478  		}
  1479  	}
  1480  	// The parser accepts much more than just the legal forms.
  1481  	return "invalid-type"
  1482  }
  1483  
  1484  func (f *file) walk(fn func(ast.Node) bool) {
  1485  	ast.Walk(walker(fn), f.f)
  1486  }
  1487  
  1488  func (f *file) render(x interface{}) string {
  1489  	var buf bytes.Buffer
  1490  	if err := printer.Fprint(&buf, f.fset, x); err != nil {
  1491  		panic(err)
  1492  	}
  1493  	return buf.String()
  1494  }
  1495  
  1496  func (f *file) debugRender(x interface{}) string {
  1497  	var buf bytes.Buffer
  1498  	if err := ast.Fprint(&buf, f.fset, x, nil); err != nil {
  1499  		panic(err)
  1500  	}
  1501  	return buf.String()
  1502  }
  1503  
  1504  // walker adapts a function to satisfy the ast.Visitor interface.
  1505  // The function return whether the walk should proceed into the node's children.
  1506  type walker func(ast.Node) bool
  1507  
  1508  func (w walker) Visit(node ast.Node) ast.Visitor {
  1509  	if w(node) {
  1510  		return w
  1511  	}
  1512  	return nil
  1513  }
  1514  
  1515  func isIdent(expr ast.Expr, ident string) bool {
  1516  	id, ok := expr.(*ast.Ident)
  1517  	return ok && id.Name == ident
  1518  }
  1519  
  1520  // isBlank returns whether id is the blank identifier "_".
  1521  // If id == nil, the answer is false.
  1522  func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
  1523  
  1524  func isPkgDot(expr ast.Expr, pkg, name string) bool {
  1525  	sel, ok := expr.(*ast.SelectorExpr)
  1526  	return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
  1527  }
  1528  
  1529  func isZero(expr ast.Expr) bool {
  1530  	lit, ok := expr.(*ast.BasicLit)
  1531  	return ok && lit.Kind == token.INT && lit.Value == "0"
  1532  }
  1533  
  1534  func isOne(expr ast.Expr) bool {
  1535  	lit, ok := expr.(*ast.BasicLit)
  1536  	return ok && lit.Kind == token.INT && lit.Value == "1"
  1537  }
  1538  
  1539  func isCgoExported(f *ast.FuncDecl) bool {
  1540  	if f.Recv != nil || f.Doc == nil {
  1541  		return false
  1542  	}
  1543  
  1544  	cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
  1545  	for _, c := range f.Doc.List {
  1546  		if cgoExport.MatchString(c.Text) {
  1547  			return true
  1548  		}
  1549  	}
  1550  	return false
  1551  }
  1552  
  1553  var basicTypeKinds = map[types.BasicKind]string{
  1554  	types.UntypedBool:    "bool",
  1555  	types.UntypedInt:     "int",
  1556  	types.UntypedRune:    "rune",
  1557  	types.UntypedFloat:   "float64",
  1558  	types.UntypedComplex: "complex128",
  1559  	types.UntypedString:  "string",
  1560  }
  1561  
  1562  // isUntypedConst reports whether expr is an untyped constant,
  1563  // and indicates what its default type is.
  1564  // scope may be nil.
  1565  func (f *file) isUntypedConst(expr ast.Expr) (defType string, ok bool) {
  1566  	// Re-evaluate expr outside of its context to see if it's untyped.
  1567  	// (An expr evaluated within, for example, an assignment context will get the type of the LHS.)
  1568  	exprStr := f.render(expr)
  1569  	tv, err := types.Eval(f.fset, f.pkg.typesPkg, expr.Pos(), exprStr)
  1570  	if err != nil {
  1571  		return "", false
  1572  	}
  1573  	if b, ok := tv.Type.(*types.Basic); ok {
  1574  		if dt, ok := basicTypeKinds[b.Kind()]; ok {
  1575  			return dt, true
  1576  		}
  1577  	}
  1578  
  1579  	return "", false
  1580  }
  1581  
  1582  // firstLineOf renders the given node and returns its first line.
  1583  // It will also match the indentation of another node.
  1584  func (f *file) firstLineOf(node, match ast.Node) string {
  1585  	line := f.render(node)
  1586  	if i := strings.Index(line, "\n"); i >= 0 {
  1587  		line = line[:i]
  1588  	}
  1589  	return f.indentOf(match) + line
  1590  }
  1591  
  1592  func (f *file) indentOf(node ast.Node) string {
  1593  	line := srcLine(f.src, f.fset.Position(node.Pos()))
  1594  	for i, r := range line {
  1595  		switch r {
  1596  		case ' ', '\t':
  1597  		default:
  1598  			return line[:i]
  1599  		}
  1600  	}
  1601  	return line // unusual or empty line
  1602  }
  1603  
  1604  func (f *file) srcLineWithMatch(node ast.Node, pattern string) (m []string) {
  1605  	line := srcLine(f.src, f.fset.Position(node.Pos()))
  1606  	line = strings.TrimSuffix(line, "\n")
  1607  	rx := regexp.MustCompile(pattern)
  1608  	return rx.FindStringSubmatch(line)
  1609  }
  1610  
  1611  // srcLine returns the complete line at p, including the terminating newline.
  1612  func srcLine(src []byte, p token.Position) string {
  1613  	// Run to end of line in both directions if not at line start/end.
  1614  	lo, hi := p.Offset, p.Offset+1
  1615  	for lo > 0 && src[lo-1] != '\n' {
  1616  		lo--
  1617  	}
  1618  	for hi < len(src) && src[hi-1] != '\n' {
  1619  		hi++
  1620  	}
  1621  	return string(src[lo:hi])
  1622  }