lint.go raw

   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 // import "golang.org/x/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/ast/astutil"
  27  	"golang.org/x/tools/go/gcexportdata"
  28  )
  29  
  30  const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
  31  
  32  // A Linter lints Go source code.
  33  type Linter struct {
  34  }
  35  
  36  // Problem represents a problem in some source code.
  37  type Problem struct {
  38  	Position   token.Position // position in source file
  39  	Text       string         // the prose that describes the problem
  40  	Link       string         // (optional) the link to the style guide for the problem
  41  	Confidence float64        // a value in (0,1] estimating the confidence in this problem's correctness
  42  	LineText   string         // the source line
  43  	Category   string         // a short name for the general category of the problem
  44  
  45  	// If the problem has a suggested fix (the minority case),
  46  	// ReplacementLine is a full replacement for the relevant line of the source file.
  47  	ReplacementLine string
  48  }
  49  
  50  func (p *Problem) String() string {
  51  	if p.Link != "" {
  52  		return p.Text + "\n\n" + p.Link
  53  	}
  54  	return p.Text
  55  }
  56  
  57  type byPosition []Problem
  58  
  59  func (p byPosition) Len() int      { return len(p) }
  60  func (p byPosition) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  61  
  62  func (p byPosition) Less(i, j int) bool {
  63  	pi, pj := p[i].Position, p[j].Position
  64  
  65  	if pi.Filename != pj.Filename {
  66  		return pi.Filename < pj.Filename
  67  	}
  68  	if pi.Line != pj.Line {
  69  		return pi.Line < pj.Line
  70  	}
  71  	if pi.Column != pj.Column {
  72  		return pi.Column < pj.Column
  73  	}
  74  
  75  	return p[i].Text < p[j].Text
  76  }
  77  
  78  // Lint lints src.
  79  func (l *Linter) Lint(filename string, src []byte) ([]Problem, error) {
  80  	return l.LintFiles(map[string][]byte{filename: src})
  81  }
  82  
  83  // LintFiles lints a set of files of a single package.
  84  // The argument is a map of filename to source.
  85  func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) {
  86  	pkg := &pkg{
  87  		fset:  token.NewFileSet(),
  88  		files: make(map[string]*file),
  89  	}
  90  	var pkgName string
  91  	for filename, src := range files {
  92  		if isGenerated(src) {
  93  			continue // See issue #239
  94  		}
  95  		f, err := parser.ParseFile(pkg.fset, filename, src, parser.ParseComments)
  96  		if err != nil {
  97  			return nil, err
  98  		}
  99  		if pkgName == "" {
 100  			pkgName = f.Name.Name
 101  		} else if f.Name.Name != pkgName {
 102  			return nil, fmt.Errorf("%s is in package %s, not %s", filename, f.Name.Name, pkgName)
 103  		}
 104  		pkg.files[filename] = &file{
 105  			pkg:      pkg,
 106  			f:        f,
 107  			fset:     pkg.fset,
 108  			src:      src,
 109  			filename: filename,
 110  		}
 111  	}
 112  	if len(pkg.files) == 0 {
 113  		return nil, nil
 114  	}
 115  	return pkg.lint(), nil
 116  }
 117  
 118  var (
 119  	genHdr = []byte("// Code generated ")
 120  	genFtr = []byte(" DO NOT EDIT.")
 121  )
 122  
 123  // isGenerated reports whether the source file is generated code
 124  // according the rules from https://golang.org/s/generatedcode.
 125  func isGenerated(src []byte) bool {
 126  	sc := bufio.NewScanner(bytes.NewReader(src))
 127  	for sc.Scan() {
 128  		b := sc.Bytes()
 129  		if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
 130  			return true
 131  		}
 132  	}
 133  	return false
 134  }
 135  
 136  // pkg represents a package being linted.
 137  type pkg struct {
 138  	fset  *token.FileSet
 139  	files map[string]*file
 140  
 141  	typesPkg  *types.Package
 142  	typesInfo *types.Info
 143  
 144  	// sortable is the set of types in the package that implement sort.Interface.
 145  	sortable map[string]bool
 146  	// main is whether this is a "main" package.
 147  	main bool
 148  
 149  	problems []Problem
 150  }
 151  
 152  func (p *pkg) lint() []Problem {
 153  	if err := p.typeCheck(); err != nil {
 154  		/* TODO(dsymonds): Consider reporting these errors when golint operates on entire packages.
 155  		if e, ok := err.(types.Error); ok {
 156  			pos := p.fset.Position(e.Pos)
 157  			conf := 1.0
 158  			if strings.Contains(e.Msg, "can't find import: ") {
 159  				// Golint is probably being run in a context that doesn't support
 160  				// typechecking (e.g. package files aren't found), so don't warn about it.
 161  				conf = 0
 162  			}
 163  			if conf > 0 {
 164  				p.errorfAt(pos, conf, category("typechecking"), e.Msg)
 165  			}
 166  
 167  			// TODO(dsymonds): Abort if !e.Soft?
 168  		}
 169  		*/
 170  	}
 171  
 172  	p.scanSortable()
 173  	p.main = p.isMain()
 174  
 175  	for _, f := range p.files {
 176  		f.lint()
 177  	}
 178  
 179  	sort.Sort(byPosition(p.problems))
 180  
 181  	return p.problems
 182  }
 183  
 184  // file represents a file being linted.
 185  type file struct {
 186  	pkg      *pkg
 187  	f        *ast.File
 188  	fset     *token.FileSet
 189  	src      []byte
 190  	filename string
 191  }
 192  
 193  func (f *file) isTest() bool { return strings.HasSuffix(f.filename, "_test.go") }
 194  
 195  func (f *file) lint() {
 196  	f.lintPackageComment()
 197  	f.lintImports()
 198  	f.lintBlankImports()
 199  	f.lintExported()
 200  	f.lintNames()
 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 (
 530  	allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
 531  	anyCapsRE = regexp.MustCompile(`[A-Z]`)
 532  )
 533  
 534  // knownNameExceptions is a set of names that are known to be exempt from naming checks.
 535  // This is usually because they are constrained by having to match names in the
 536  // standard library.
 537  var knownNameExceptions = map[string]bool{
 538  	"LastInsertId": true, // must match database/sql
 539  	"kWh":          true,
 540  }
 541  
 542  func isInTopLevel(f *ast.File, ident *ast.Ident) bool {
 543  	path, _ := astutil.PathEnclosingInterval(f, ident.Pos(), ident.End())
 544  	for _, f := range path {
 545  		switch f.(type) {
 546  		case *ast.File, *ast.GenDecl, *ast.ValueSpec, *ast.Ident:
 547  			continue
 548  		}
 549  		return false
 550  	}
 551  	return true
 552  }
 553  
 554  // lintNames examines all names in the file.
 555  // It complains if any use underscores or incorrect known initialisms.
 556  func (f *file) lintNames() {
 557  	// Package names need slightly different handling than other names.
 558  	if strings.Contains(f.f.Name.Name, "_") && !strings.HasSuffix(f.f.Name.Name, "_test") {
 559  		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")
 560  	}
 561  	if anyCapsRE.MatchString(f.f.Name.Name) {
 562  		f.errorf(f.f, 1, link("http://golang.org/doc/effective_go.html#package-names"), category("mixed-caps"), "don't use MixedCaps in package name; %s should be %s", f.f.Name.Name, strings.ToLower(f.f.Name.Name))
 563  	}
 564  
 565  	check := func(id *ast.Ident, thing string) {
 566  		if id.Name == "_" {
 567  			return
 568  		}
 569  		if knownNameExceptions[id.Name] {
 570  			return
 571  		}
 572  
 573  		// Handle two common styles from other languages that don't belong in Go.
 574  		if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") {
 575  			capCount := 0
 576  			for _, c := range id.Name {
 577  				if 'A' <= c && c <= 'Z' {
 578  					capCount++
 579  				}
 580  			}
 581  			if capCount >= 2 {
 582  				f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use ALL_CAPS in Go names; use CamelCase")
 583  				return
 584  			}
 585  		}
 586  		if thing == "const" || (thing == "var" && isInTopLevel(f.f, id)) {
 587  			if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
 588  				should := string(id.Name[1]+'a'-'A') + id.Name[2:]
 589  				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)
 590  			}
 591  		}
 592  
 593  		should := lintName(id.Name)
 594  		if id.Name == should {
 595  			return
 596  		}
 597  
 598  		if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") {
 599  			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)
 600  			return
 601  		}
 602  		f.errorf(id, 0.8, link(styleGuideBase+"#initialisms"), category("naming"), "%s %s should be %s", thing, id.Name, should)
 603  	}
 604  	checkList := func(fl *ast.FieldList, thing string) {
 605  		if fl == nil {
 606  			return
 607  		}
 608  		for _, f := range fl.List {
 609  			for _, id := range f.Names {
 610  				check(id, thing)
 611  			}
 612  		}
 613  	}
 614  	f.walk(func(node ast.Node) bool {
 615  		switch v := node.(type) {
 616  		case *ast.AssignStmt:
 617  			if v.Tok == token.ASSIGN {
 618  				return true
 619  			}
 620  			for _, exp := range v.Lhs {
 621  				if id, ok := exp.(*ast.Ident); ok {
 622  					check(id, "var")
 623  				}
 624  			}
 625  		case *ast.FuncDecl:
 626  			if f.isTest() && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
 627  				return true
 628  			}
 629  
 630  			thing := "func"
 631  			if v.Recv != nil {
 632  				thing = "method"
 633  			}
 634  
 635  			// Exclude naming warnings for functions that are exported to C but
 636  			// not exported in the Go API.
 637  			// See https://github.com/golang/lint/issues/144.
 638  			if ast.IsExported(v.Name.Name) || !isCgoExported(v) {
 639  				check(v.Name, thing)
 640  			}
 641  
 642  			checkList(v.Type.Params, thing+" parameter")
 643  			checkList(v.Type.Results, thing+" result")
 644  		case *ast.GenDecl:
 645  			if v.Tok == token.IMPORT {
 646  				return true
 647  			}
 648  			var thing string
 649  			switch v.Tok {
 650  			case token.CONST:
 651  				thing = "const"
 652  			case token.TYPE:
 653  				thing = "type"
 654  			case token.VAR:
 655  				thing = "var"
 656  			}
 657  			for _, spec := range v.Specs {
 658  				switch s := spec.(type) {
 659  				case *ast.TypeSpec:
 660  					check(s.Name, thing)
 661  				case *ast.ValueSpec:
 662  					for _, id := range s.Names {
 663  						check(id, thing)
 664  					}
 665  				}
 666  			}
 667  		case *ast.InterfaceType:
 668  			// Do not check interface method names.
 669  			// They are often constrainted by the method names of concrete types.
 670  			for _, x := range v.Methods.List {
 671  				ft, ok := x.Type.(*ast.FuncType)
 672  				if !ok { // might be an embedded interface name
 673  					continue
 674  				}
 675  				checkList(ft.Params, "interface method parameter")
 676  				checkList(ft.Results, "interface method result")
 677  			}
 678  		case *ast.RangeStmt:
 679  			if v.Tok == token.ASSIGN {
 680  				return true
 681  			}
 682  			if id, ok := v.Key.(*ast.Ident); ok {
 683  				check(id, "range var")
 684  			}
 685  			if id, ok := v.Value.(*ast.Ident); ok {
 686  				check(id, "range var")
 687  			}
 688  		case *ast.StructType:
 689  			for _, f := range v.Fields.List {
 690  				for _, id := range f.Names {
 691  					check(id, "struct field")
 692  				}
 693  			}
 694  		}
 695  		return true
 696  	})
 697  }
 698  
 699  // lintName returns a different name if it should be different.
 700  func lintName(name string) (should string) {
 701  	// Fast path for simple cases: "_" and all lowercase.
 702  	if name == "_" {
 703  		return name
 704  	}
 705  	allLower := true
 706  	for _, r := range name {
 707  		if !unicode.IsLower(r) {
 708  			allLower = false
 709  			break
 710  		}
 711  	}
 712  	if allLower {
 713  		return name
 714  	}
 715  
 716  	// Split camelCase at any lower->upper transition, and split on underscores.
 717  	// Check each word for common initialisms.
 718  	runes := []rune(name)
 719  	w, i := 0, 0 // index of start of word, scan
 720  	for i+1 <= len(runes) {
 721  		eow := false // whether we hit the end of a word
 722  		if i+1 == len(runes) {
 723  			eow = true
 724  		} else if runes[i+1] == '_' {
 725  			// underscore; shift the remainder forward over any run of underscores
 726  			eow = true
 727  			n := 1
 728  			for i+n+1 < len(runes) && runes[i+n+1] == '_' {
 729  				n++
 730  			}
 731  
 732  			// Leave at most one underscore if the underscore is between two digits
 733  			if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
 734  				n--
 735  			}
 736  
 737  			copy(runes[i+1:], runes[i+n+1:])
 738  			runes = runes[:len(runes)-n]
 739  		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
 740  			// lower->non-lower
 741  			eow = true
 742  		}
 743  		i++
 744  		if !eow {
 745  			continue
 746  		}
 747  
 748  		// [w,i) is a word.
 749  		word := string(runes[w:i])
 750  		if u := strings.ToUpper(word); commonInitialisms[u] {
 751  			// Keep consistent case, which is lowercase only at the start.
 752  			if w == 0 && unicode.IsLower(runes[w]) {
 753  				u = strings.ToLower(u)
 754  			}
 755  			// All the common initialisms are ASCII,
 756  			// so we can replace the bytes exactly.
 757  			copy(runes[w:], []rune(u))
 758  		} else if w > 0 && strings.ToLower(word) == word {
 759  			// already all lowercase, and not the first word, so uppercase the first character.
 760  			runes[w] = unicode.ToUpper(runes[w])
 761  		}
 762  		w = i
 763  	}
 764  	return string(runes)
 765  }
 766  
 767  // commonInitialisms is a set of common initialisms.
 768  // Only add entries that are highly unlikely to be non-initialisms.
 769  // For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
 770  var commonInitialisms = map[string]bool{
 771  	"ACL":   true,
 772  	"API":   true,
 773  	"ASCII": true,
 774  	"CPU":   true,
 775  	"CSS":   true,
 776  	"DNS":   true,
 777  	"EOF":   true,
 778  	"GUID":  true,
 779  	"HTML":  true,
 780  	"HTTP":  true,
 781  	"HTTPS": true,
 782  	"ID":    true,
 783  	"IP":    true,
 784  	"JSON":  true,
 785  	"LHS":   true,
 786  	"QPS":   true,
 787  	"RAM":   true,
 788  	"RHS":   true,
 789  	"RPC":   true,
 790  	"SLA":   true,
 791  	"SMTP":  true,
 792  	"SQL":   true,
 793  	"SSH":   true,
 794  	"TCP":   true,
 795  	"TLS":   true,
 796  	"TTL":   true,
 797  	"UDP":   true,
 798  	"UI":    true,
 799  	"UID":   true,
 800  	"UUID":  true,
 801  	"URI":   true,
 802  	"URL":   true,
 803  	"UTF8":  true,
 804  	"VM":    true,
 805  	"XML":   true,
 806  	"XMPP":  true,
 807  	"XSRF":  true,
 808  	"XSS":   true,
 809  }
 810  
 811  // lintTypeDoc examines the doc comment on a type.
 812  // It complains if they are missing from an exported type,
 813  // or if they are not of the standard form.
 814  func (f *file) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) {
 815  	if !ast.IsExported(t.Name.Name) {
 816  		return
 817  	}
 818  	if doc == nil {
 819  		f.errorf(t, 1, link(docCommentsLink), category("comments"), "exported type %v should have comment or be unexported", t.Name)
 820  		return
 821  	}
 822  
 823  	s := doc.Text()
 824  	articles := [...]string{"A", "An", "The"}
 825  	for _, a := range articles {
 826  		if strings.HasPrefix(s, a+" ") {
 827  			s = s[len(a)+1:]
 828  			break
 829  		}
 830  	}
 831  	if !strings.HasPrefix(s, t.Name.Name+" ") {
 832  		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)
 833  	}
 834  }
 835  
 836  var commonMethods = map[string]bool{
 837  	"Error":     true,
 838  	"Read":      true,
 839  	"ServeHTTP": true,
 840  	"String":    true,
 841  	"Write":     true,
 842  	"Unwrap":    true,
 843  }
 844  
 845  // lintFuncDoc examines doc comments on functions and methods.
 846  // It complains if they are missing, or not of the right form.
 847  // It has specific exclusions for well-known methods (see commonMethods above).
 848  func (f *file) lintFuncDoc(fn *ast.FuncDecl) {
 849  	if !ast.IsExported(fn.Name.Name) {
 850  		// func is unexported
 851  		return
 852  	}
 853  	kind := "function"
 854  	name := fn.Name.Name
 855  	if fn.Recv != nil && len(fn.Recv.List) > 0 {
 856  		// method
 857  		kind = "method"
 858  		recv := receiverType(fn)
 859  		if !ast.IsExported(recv) {
 860  			// receiver is unexported
 861  			return
 862  		}
 863  		if commonMethods[name] {
 864  			return
 865  		}
 866  		switch name {
 867  		case "Len", "Less", "Swap":
 868  			if f.pkg.sortable[recv] {
 869  				return
 870  			}
 871  		}
 872  		name = recv + "." + name
 873  	}
 874  	if fn.Doc == nil {
 875  		f.errorf(fn, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment or be unexported", kind, name)
 876  		return
 877  	}
 878  	s := fn.Doc.Text()
 879  	prefix := fn.Name.Name + " "
 880  	if !strings.HasPrefix(s, prefix) {
 881  		f.errorf(fn.Doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
 882  	}
 883  }
 884  
 885  // lintValueSpecDoc examines package-global variables and constants.
 886  // It complains if they are not individually declared,
 887  // or if they are not suitably documented in the right form (unless they are in a block that is commented).
 888  func (f *file) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) {
 889  	kind := "var"
 890  	if gd.Tok == token.CONST {
 891  		kind = "const"
 892  	}
 893  
 894  	if len(vs.Names) > 1 {
 895  		// Check that none are exported except for the first.
 896  		for _, n := range vs.Names[1:] {
 897  			if ast.IsExported(n.Name) {
 898  				f.errorf(vs, 1, category("comments"), "exported %s %s should have its own declaration", kind, n.Name)
 899  				return
 900  			}
 901  		}
 902  	}
 903  
 904  	// Only one name.
 905  	name := vs.Names[0].Name
 906  	if !ast.IsExported(name) {
 907  		return
 908  	}
 909  
 910  	if vs.Doc == nil && gd.Doc == nil {
 911  		if genDeclMissingComments[gd] {
 912  			return
 913  		}
 914  		block := ""
 915  		if kind == "const" && gd.Lparen.IsValid() {
 916  			block = " (or a comment on this block)"
 917  		}
 918  		f.errorf(vs, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment%s or be unexported", kind, name, block)
 919  		genDeclMissingComments[gd] = true
 920  		return
 921  	}
 922  	// If this GenDecl has parens and a comment, we don't check its comment form.
 923  	if gd.Lparen.IsValid() && gd.Doc != nil {
 924  		return
 925  	}
 926  	// The relevant text to check will be on either vs.Doc or gd.Doc.
 927  	// Use vs.Doc preferentially.
 928  	doc := vs.Doc
 929  	if doc == nil {
 930  		doc = gd.Doc
 931  	}
 932  	prefix := name + " "
 933  	if !strings.HasPrefix(doc.Text(), prefix) {
 934  		f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
 935  	}
 936  }
 937  
 938  func (f *file) checkStutter(id *ast.Ident, thing string) {
 939  	pkg, name := f.f.Name.Name, id.Name
 940  	if !ast.IsExported(name) {
 941  		// unexported name
 942  		return
 943  	}
 944  	// A name stutters if the package name is a strict prefix
 945  	// and the next character of the name starts a new word.
 946  	if len(name) <= len(pkg) {
 947  		// name is too short to stutter.
 948  		// This permits the name to be the same as the package name.
 949  		return
 950  	}
 951  	if !strings.EqualFold(pkg, name[:len(pkg)]) {
 952  		return
 953  	}
 954  	// We can assume the name is well-formed UTF-8.
 955  	// If the next rune after the package name is uppercase or an underscore
 956  	// the it's starting a new word and thus this name stutters.
 957  	rem := name[len(pkg):]
 958  	if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) {
 959  		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)
 960  	}
 961  }
 962  
 963  // zeroLiteral is a set of ast.BasicLit values that are zero values.
 964  // It is not exhaustive.
 965  var zeroLiteral = map[string]bool{
 966  	"false": true, // bool
 967  	// runes
 968  	`'\x00'`: true,
 969  	`'\000'`: true,
 970  	// strings
 971  	`""`: true,
 972  	"``": true,
 973  	// numerics
 974  	"0":   true,
 975  	"0.":  true,
 976  	"0.0": true,
 977  	"0i":  true,
 978  }
 979  
 980  // lintElses examines else blocks. It complains about any else block whose if block ends in a return.
 981  func (f *file) lintElses() {
 982  	// We don't want to flag if { } else if { } else { } constructions.
 983  	// They will appear as an IfStmt whose Else field is also an IfStmt.
 984  	// Record such a node so we ignore it when we visit it.
 985  	ignore := make(map[*ast.IfStmt]bool)
 986  
 987  	f.walk(func(node ast.Node) bool {
 988  		ifStmt, ok := node.(*ast.IfStmt)
 989  		if !ok || ifStmt.Else == nil {
 990  			return true
 991  		}
 992  		if elseif, ok := ifStmt.Else.(*ast.IfStmt); ok {
 993  			ignore[elseif] = true
 994  			return true
 995  		}
 996  		if ignore[ifStmt] {
 997  			return true
 998  		}
 999  		if _, ok := ifStmt.Else.(*ast.BlockStmt); !ok {
1000  			// only care about elses without conditions
1001  			return true
1002  		}
1003  		if len(ifStmt.Body.List) == 0 {
1004  			return true
1005  		}
1006  		shortDecl := false // does the if statement have a ":=" initialization statement?
1007  		if ifStmt.Init != nil {
1008  			if as, ok := ifStmt.Init.(*ast.AssignStmt); ok && as.Tok == token.DEFINE {
1009  				shortDecl = true
1010  			}
1011  		}
1012  		lastStmt := ifStmt.Body.List[len(ifStmt.Body.List)-1]
1013  		if _, ok := lastStmt.(*ast.ReturnStmt); ok {
1014  			extra := ""
1015  			if shortDecl {
1016  				extra = " (move short variable declaration to its own line if necessary)"
1017  			}
1018  			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)
1019  		}
1020  		return true
1021  	})
1022  }
1023  
1024  // lintRanges examines range clauses. It complains about redundant constructions.
1025  func (f *file) lintRanges() {
1026  	f.walk(func(node ast.Node) bool {
1027  		rs, ok := node.(*ast.RangeStmt)
1028  		if !ok {
1029  			return true
1030  		}
1031  
1032  		if isIdent(rs.Key, "_") && (rs.Value == nil || isIdent(rs.Value, "_")) {
1033  			p := f.errorf(rs.Key, 1, category("range-loop"), "should omit values from range; this loop is equivalent to `for range ...`")
1034  
1035  			newRS := *rs // shallow copy
1036  			newRS.Value = nil
1037  			newRS.Key = nil
1038  			p.ReplacementLine = f.firstLineOf(&newRS, rs)
1039  
1040  			return true
1041  		}
1042  
1043  		if isIdent(rs.Value, "_") {
1044  			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)
1045  
1046  			newRS := *rs // shallow copy
1047  			newRS.Value = nil
1048  			p.ReplacementLine = f.firstLineOf(&newRS, rs)
1049  		}
1050  
1051  		return true
1052  	})
1053  }
1054  
1055  // lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation.
1056  func (f *file) lintErrorf() {
1057  	f.walk(func(node ast.Node) bool {
1058  		ce, ok := node.(*ast.CallExpr)
1059  		if !ok || len(ce.Args) != 1 {
1060  			return true
1061  		}
1062  		isErrorsNew := isPkgDot(ce.Fun, "errors", "New")
1063  		var isTestingError bool
1064  		se, ok := ce.Fun.(*ast.SelectorExpr)
1065  		if ok && se.Sel.Name == "Error" {
1066  			if typ := f.pkg.typeOf(se.X); typ != nil {
1067  				isTestingError = typ.String() == "*testing.T"
1068  			}
1069  		}
1070  		if !isErrorsNew && !isTestingError {
1071  			return true
1072  		}
1073  		if !f.imports("errors") {
1074  			return true
1075  		}
1076  		arg := ce.Args[0]
1077  		ce, ok = arg.(*ast.CallExpr)
1078  		if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
1079  			return true
1080  		}
1081  		errorfPrefix := "fmt"
1082  		if isTestingError {
1083  			errorfPrefix = f.render(se.X)
1084  		}
1085  		p := f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix)
1086  
1087  		m := f.srcLineWithMatch(ce, `^(.*)`+f.render(se)+`\(fmt\.Sprintf\((.*)\)\)(.*)$`)
1088  		if m != nil {
1089  			p.ReplacementLine = m[1] + errorfPrefix + ".Errorf(" + m[2] + ")" + m[3]
1090  		}
1091  
1092  		return true
1093  	})
1094  }
1095  
1096  // lintErrors examines global error vars. It complains if they aren't named in the standard way.
1097  func (f *file) lintErrors() {
1098  	for _, decl := range f.f.Decls {
1099  		gd, ok := decl.(*ast.GenDecl)
1100  		if !ok || gd.Tok != token.VAR {
1101  			continue
1102  		}
1103  		for _, spec := range gd.Specs {
1104  			spec := spec.(*ast.ValueSpec)
1105  			if len(spec.Names) != 1 || len(spec.Values) != 1 {
1106  				continue
1107  			}
1108  			ce, ok := spec.Values[0].(*ast.CallExpr)
1109  			if !ok {
1110  				continue
1111  			}
1112  			if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
1113  				continue
1114  			}
1115  
1116  			id := spec.Names[0]
1117  			prefix := "err"
1118  			if id.IsExported() {
1119  				prefix = "Err"
1120  			}
1121  			if !strings.HasPrefix(id.Name, prefix) {
1122  				f.errorf(id, 0.9, category("naming"), "error var %s should have name of the form %sFoo", id.Name, prefix)
1123  			}
1124  		}
1125  	}
1126  }
1127  
1128  func lintErrorString(s string) (isClean bool, conf float64) {
1129  	const basicConfidence = 0.8
1130  	const capConfidence = basicConfidence - 0.2
1131  	first, firstN := utf8.DecodeRuneInString(s)
1132  	last, _ := utf8.DecodeLastRuneInString(s)
1133  	if last == '.' || last == ':' || last == '!' || last == '\n' {
1134  		return false, basicConfidence
1135  	}
1136  	if unicode.IsUpper(first) {
1137  		// People use proper nouns and exported Go identifiers in error strings,
1138  		// so decrease the confidence of warnings for capitalization.
1139  		if len(s) <= firstN {
1140  			return false, capConfidence
1141  		}
1142  		// Flag strings starting with something that doesn't look like an initialism.
1143  		if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) {
1144  			return false, capConfidence
1145  		}
1146  	}
1147  	return true, 0
1148  }
1149  
1150  // lintErrorStrings examines error strings.
1151  // It complains if they are capitalized or end in punctuation or a newline.
1152  func (f *file) lintErrorStrings() {
1153  	f.walk(func(node ast.Node) bool {
1154  		ce, ok := node.(*ast.CallExpr)
1155  		if !ok {
1156  			return true
1157  		}
1158  		if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
1159  			return true
1160  		}
1161  		if len(ce.Args) < 1 {
1162  			return true
1163  		}
1164  		str, ok := ce.Args[0].(*ast.BasicLit)
1165  		if !ok || str.Kind != token.STRING {
1166  			return true
1167  		}
1168  		s, _ := strconv.Unquote(str.Value) // can assume well-formed Go
1169  		if s == "" {
1170  			return true
1171  		}
1172  		clean, conf := lintErrorString(s)
1173  		if clean {
1174  			return true
1175  		}
1176  
1177  		f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"),
1178  			"error strings should not be capitalized or end with punctuation or a newline")
1179  		return true
1180  	})
1181  }
1182  
1183  // lintReceiverNames examines receiver names. It complains about inconsistent
1184  // names used for the same type and names such as "this".
1185  func (f *file) lintReceiverNames() {
1186  	typeReceiver := map[string]string{}
1187  	f.walk(func(n ast.Node) bool {
1188  		fn, ok := n.(*ast.FuncDecl)
1189  		if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
1190  			return true
1191  		}
1192  		names := fn.Recv.List[0].Names
1193  		if len(names) < 1 {
1194  			return true
1195  		}
1196  		name := names[0].Name
1197  		const ref = styleGuideBase + "#receiver-names"
1198  		if name == "_" {
1199  			f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore, omit the name if it is unused`)
1200  			return true
1201  		}
1202  		if name == "this" || name == "self" {
1203  			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"`)
1204  			return true
1205  		}
1206  		recv := receiverType(fn)
1207  		if prev, ok := typeReceiver[recv]; ok && prev != name {
1208  			f.errorf(n, 1, link(ref), category("naming"), "receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv)
1209  			return true
1210  		}
1211  		typeReceiver[recv] = name
1212  		return true
1213  	})
1214  }
1215  
1216  // lintIncDec examines statements that increment or decrement a variable.
1217  // It complains if they don't use x++ or x--.
1218  func (f *file) lintIncDec() {
1219  	f.walk(func(n ast.Node) bool {
1220  		as, ok := n.(*ast.AssignStmt)
1221  		if !ok {
1222  			return true
1223  		}
1224  		if len(as.Lhs) != 1 {
1225  			return true
1226  		}
1227  		if !isOne(as.Rhs[0]) {
1228  			return true
1229  		}
1230  		var suffix string
1231  		switch as.Tok {
1232  		case token.ADD_ASSIGN:
1233  			suffix = "++"
1234  		case token.SUB_ASSIGN:
1235  			suffix = "--"
1236  		default:
1237  			return true
1238  		}
1239  		f.errorf(as, 0.8, category("unary-op"), "should replace %s with %s%s", f.render(as), f.render(as.Lhs[0]), suffix)
1240  		return true
1241  	})
1242  }
1243  
1244  // lintErrorReturn examines function declarations that return an error.
1245  // It complains if the error isn't the last parameter.
1246  func (f *file) lintErrorReturn() {
1247  	f.walk(func(n ast.Node) bool {
1248  		fn, ok := n.(*ast.FuncDecl)
1249  		if !ok || fn.Type.Results == nil {
1250  			return true
1251  		}
1252  		ret := fn.Type.Results.List
1253  		if len(ret) <= 1 {
1254  			return true
1255  		}
1256  		if isIdent(ret[len(ret)-1].Type, "error") {
1257  			return true
1258  		}
1259  		// An error return parameter should be the last parameter.
1260  		// Flag any error parameters found before the last.
1261  		for _, r := range ret[:len(ret)-1] {
1262  			if isIdent(r.Type, "error") {
1263  				f.errorf(fn, 0.9, category("arg-order"), "error should be the last type when returning multiple items")
1264  				break // only flag one
1265  			}
1266  		}
1267  		return true
1268  	})
1269  }
1270  
1271  // lintUnexportedReturn examines exported function declarations.
1272  // It complains if any return an unexported type.
1273  func (f *file) lintUnexportedReturn() {
1274  	f.walk(func(n ast.Node) bool {
1275  		fn, ok := n.(*ast.FuncDecl)
1276  		if !ok {
1277  			return true
1278  		}
1279  		if fn.Type.Results == nil {
1280  			return false
1281  		}
1282  		if !fn.Name.IsExported() {
1283  			return false
1284  		}
1285  		thing := "func"
1286  		if fn.Recv != nil && len(fn.Recv.List) > 0 {
1287  			thing = "method"
1288  			if !ast.IsExported(receiverType(fn)) {
1289  				// Don't report exported methods of unexported types,
1290  				// such as private implementations of sort.Interface.
1291  				return false
1292  			}
1293  		}
1294  		for _, ret := range fn.Type.Results.List {
1295  			typ := f.pkg.typeOf(ret.Type)
1296  			if exportedType(typ) {
1297  				continue
1298  			}
1299  			f.errorf(ret.Type, 0.8, category("unexported-type-in-api"),
1300  				"exported %s %s returns unexported type %s, which can be annoying to use",
1301  				thing, fn.Name.Name, typ)
1302  			break // only flag one
1303  		}
1304  		return false
1305  	})
1306  }
1307  
1308  // exportedType reports whether typ is an exported type.
1309  // It is imprecise, and will err on the side of returning true,
1310  // such as for composite types.
1311  func exportedType(typ types.Type) bool {
1312  	switch T := typ.(type) {
1313  	case *types.Named:
1314  		// Builtin types have no package.
1315  		return T.Obj().Pkg() == nil || T.Obj().Exported()
1316  	case *types.Map:
1317  		return exportedType(T.Key()) && exportedType(T.Elem())
1318  	case interface {
1319  		Elem() types.Type
1320  	}: // array, slice, pointer, chan
1321  		return exportedType(T.Elem())
1322  	}
1323  	// Be conservative about other types, such as struct, interface, etc.
1324  	return true
1325  }
1326  
1327  // timeSuffixes is a list of name suffixes that imply a time unit.
1328  // This is not an exhaustive list.
1329  var timeSuffixes = []string{
1330  	"Sec", "Secs", "Seconds",
1331  	"Msec", "Msecs",
1332  	"Milli", "Millis", "Milliseconds",
1333  	"Usec", "Usecs", "Microseconds",
1334  	"MS", "Ms",
1335  }
1336  
1337  func (f *file) lintTimeNames() {
1338  	f.walk(func(node ast.Node) bool {
1339  		v, ok := node.(*ast.ValueSpec)
1340  		if !ok {
1341  			return true
1342  		}
1343  		for _, name := range v.Names {
1344  			origTyp := f.pkg.typeOf(name)
1345  			// Look for time.Duration or *time.Duration;
1346  			// the latter is common when using flag.Duration.
1347  			typ := origTyp
1348  			if pt, ok := typ.(*types.Pointer); ok {
1349  				typ = pt.Elem()
1350  			}
1351  			if !f.pkg.isNamedType(typ, "time", "Duration") {
1352  				continue
1353  			}
1354  			suffix := ""
1355  			for _, suf := range timeSuffixes {
1356  				if strings.HasSuffix(name.Name, suf) {
1357  					suffix = suf
1358  					break
1359  				}
1360  			}
1361  			if suffix == "" {
1362  				continue
1363  			}
1364  			f.errorf(v, 0.9, category("time"), "var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix)
1365  		}
1366  		return true
1367  	})
1368  }
1369  
1370  // lintContextKeyTypes checks for call expressions to context.WithValue with
1371  // basic types used for the key argument.
1372  // See: https://golang.org/issue/17293
1373  func (f *file) lintContextKeyTypes() {
1374  	f.walk(func(node ast.Node) bool {
1375  		switch node := node.(type) {
1376  		case *ast.CallExpr:
1377  			f.checkContextKeyType(node)
1378  		}
1379  
1380  		return true
1381  	})
1382  }
1383  
1384  // checkContextKeyType reports an error if the call expression calls
1385  // context.WithValue with a key argument of basic type.
1386  func (f *file) checkContextKeyType(x *ast.CallExpr) {
1387  	sel, ok := x.Fun.(*ast.SelectorExpr)
1388  	if !ok {
1389  		return
1390  	}
1391  	pkg, ok := sel.X.(*ast.Ident)
1392  	if !ok || pkg.Name != "context" {
1393  		return
1394  	}
1395  	if sel.Sel.Name != "WithValue" {
1396  		return
1397  	}
1398  
1399  	// key is second argument to context.WithValue
1400  	if len(x.Args) != 3 {
1401  		return
1402  	}
1403  	key := f.pkg.typesInfo.Types[x.Args[1]]
1404  
1405  	if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid {
1406  		f.errorf(x, 1.0, category("context"), fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type))
1407  	}
1408  }
1409  
1410  // lintContextArgs examines function declarations that contain an
1411  // argument with a type of context.Context
1412  // It complains if that argument isn't the first parameter.
1413  func (f *file) lintContextArgs() {
1414  	f.walk(func(n ast.Node) bool {
1415  		fn, ok := n.(*ast.FuncDecl)
1416  		if !ok || len(fn.Type.Params.List) <= 1 {
1417  			return true
1418  		}
1419  		// A context.Context should be the first parameter of a function.
1420  		// Flag any that show up after the first.
1421  		for _, arg := range fn.Type.Params.List[1:] {
1422  			if isPkgDot(arg.Type, "context", "Context") {
1423  				f.errorf(fn, 0.9, link("https://golang.org/pkg/context/"), category("arg-order"), "context.Context should be the first parameter of a function")
1424  				break // only flag one
1425  			}
1426  		}
1427  		return true
1428  	})
1429  }
1430  
1431  // containsComments returns whether the interval [start, end) contains any
1432  // comments without "// MATCH " prefix.
1433  func (f *file) containsComments(start, end token.Pos) bool {
1434  	for _, cgroup := range f.f.Comments {
1435  		comments := cgroup.List
1436  		if comments[0].Slash >= end {
1437  			// All comments starting with this group are after end pos.
1438  			return false
1439  		}
1440  		if comments[len(comments)-1].Slash < start {
1441  			// Comments group ends before start pos.
1442  			continue
1443  		}
1444  		for _, c := range comments {
1445  			if start <= c.Slash && c.Slash < end && !strings.HasPrefix(c.Text, "// MATCH ") {
1446  				return true
1447  			}
1448  		}
1449  	}
1450  	return false
1451  }
1452  
1453  // receiverType returns the named type of the method receiver, sans "*",
1454  // or "invalid-type" if fn.Recv is ill formed.
1455  func receiverType(fn *ast.FuncDecl) string {
1456  	switch e := fn.Recv.List[0].Type.(type) {
1457  	case *ast.Ident:
1458  		return e.Name
1459  	case *ast.StarExpr:
1460  		if id, ok := e.X.(*ast.Ident); ok {
1461  			return id.Name
1462  		}
1463  	}
1464  	// The parser accepts much more than just the legal forms.
1465  	return "invalid-type"
1466  }
1467  
1468  func (f *file) walk(fn func(ast.Node) bool) {
1469  	ast.Walk(walker(fn), f.f)
1470  }
1471  
1472  func (f *file) render(x interface{}) string {
1473  	var buf bytes.Buffer
1474  	if err := printer.Fprint(&buf, f.fset, x); err != nil {
1475  		panic(err)
1476  	}
1477  	return buf.String()
1478  }
1479  
1480  func (f *file) debugRender(x interface{}) string {
1481  	var buf bytes.Buffer
1482  	if err := ast.Fprint(&buf, f.fset, x, nil); err != nil {
1483  		panic(err)
1484  	}
1485  	return buf.String()
1486  }
1487  
1488  // walker adapts a function to satisfy the ast.Visitor interface.
1489  // The function return whether the walk should proceed into the node's children.
1490  type walker func(ast.Node) bool
1491  
1492  func (w walker) Visit(node ast.Node) ast.Visitor {
1493  	if w(node) {
1494  		return w
1495  	}
1496  	return nil
1497  }
1498  
1499  func isIdent(expr ast.Expr, ident string) bool {
1500  	id, ok := expr.(*ast.Ident)
1501  	return ok && id.Name == ident
1502  }
1503  
1504  // isBlank returns whether id is the blank identifier "_".
1505  // If id == nil, the answer is false.
1506  func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
1507  
1508  func isPkgDot(expr ast.Expr, pkg, name string) bool {
1509  	sel, ok := expr.(*ast.SelectorExpr)
1510  	return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
1511  }
1512  
1513  func isOne(expr ast.Expr) bool {
1514  	lit, ok := expr.(*ast.BasicLit)
1515  	return ok && lit.Kind == token.INT && lit.Value == "1"
1516  }
1517  
1518  func isCgoExported(f *ast.FuncDecl) bool {
1519  	if f.Recv != nil || f.Doc == nil {
1520  		return false
1521  	}
1522  
1523  	cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
1524  	for _, c := range f.Doc.List {
1525  		if cgoExport.MatchString(c.Text) {
1526  			return true
1527  		}
1528  	}
1529  	return false
1530  }
1531  
1532  var basicTypeKinds = map[types.BasicKind]string{
1533  	types.UntypedBool:    "bool",
1534  	types.UntypedInt:     "int",
1535  	types.UntypedRune:    "rune",
1536  	types.UntypedFloat:   "float64",
1537  	types.UntypedComplex: "complex128",
1538  	types.UntypedString:  "string",
1539  }
1540  
1541  // isUntypedConst reports whether expr is an untyped constant,
1542  // and indicates what its default type is.
1543  // scope may be nil.
1544  func (f *file) isUntypedConst(expr ast.Expr) (defType string, ok bool) {
1545  	// Re-evaluate expr outside of its context to see if it's untyped.
1546  	// (An expr evaluated within, for example, an assignment context will get the type of the LHS.)
1547  	exprStr := f.render(expr)
1548  	tv, err := types.Eval(f.fset, f.pkg.typesPkg, expr.Pos(), exprStr)
1549  	if err != nil {
1550  		return "", false
1551  	}
1552  	if b, ok := tv.Type.(*types.Basic); ok {
1553  		if dt, ok := basicTypeKinds[b.Kind()]; ok {
1554  			return dt, true
1555  		}
1556  	}
1557  
1558  	return "", false
1559  }
1560  
1561  // firstLineOf renders the given node and returns its first line.
1562  // It will also match the indentation of another node.
1563  func (f *file) firstLineOf(node, match ast.Node) string {
1564  	line := f.render(node)
1565  	if i := strings.Index(line, "\n"); i >= 0 {
1566  		line = line[:i]
1567  	}
1568  	return f.indentOf(match) + line
1569  }
1570  
1571  func (f *file) indentOf(node ast.Node) string {
1572  	line := srcLine(f.src, f.fset.Position(node.Pos()))
1573  	for i, r := range line {
1574  		switch r {
1575  		case ' ', '\t':
1576  		default:
1577  			return line[:i]
1578  		}
1579  	}
1580  	return line // unusual or empty line
1581  }
1582  
1583  func (f *file) srcLineWithMatch(node ast.Node, pattern string) (m []string) {
1584  	line := srcLine(f.src, f.fset.Position(node.Pos()))
1585  	line = strings.TrimSuffix(line, "\n")
1586  	rx := regexp.MustCompile(pattern)
1587  	return rx.FindStringSubmatch(line)
1588  }
1589  
1590  // imports returns true if the current file imports the specified package path.
1591  func (f *file) imports(importPath string) bool {
1592  	all := astutil.Imports(f.fset, f.f)
1593  	for _, p := range all {
1594  		for _, i := range p {
1595  			uq, err := strconv.Unquote(i.Path.Value)
1596  			if err == nil && importPath == uq {
1597  				return true
1598  			}
1599  		}
1600  	}
1601  	return false
1602  }
1603  
1604  // srcLine returns the complete line at p, including the terminating newline.
1605  func srcLine(src []byte, p token.Position) string {
1606  	// Run to end of line in both directions if not at line start/end.
1607  	lo, hi := p.Offset, p.Offset+1
1608  	for lo > 0 && src[lo-1] != '\n' {
1609  		lo--
1610  	}
1611  	for hi < len(src) && src[hi-1] != '\n' {
1612  		hi++
1613  	}
1614  	return string(src[lo:hi])
1615  }
1616