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.
8package lint // import "golang.org/x/lint"
9
10import (
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
30const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
31
32// A Linter lints Go source code.
33type Linter struct {
34}
35
36// Problem represents a problem in some source code.
37type 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
50func (p *Problem) String() string {
51	if p.Link != "" {
52		return p.Text + "\n\n" + p.Link
53	}
54	return p.Text
55}
56
57type byPosition []Problem
58
59func (p byPosition) Len() int      { return len(p) }
60func (p byPosition) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
61
62func (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.
79func (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.
85func (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
118var (
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.
125func 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.
137type 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
152func (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.
185type file struct {
186	pkg      *pkg
187	f        *ast.File
188	fset     *token.FileSet
189	src      []byte
190	filename string
191}
192
193func (f *file) isTest() bool { return strings.HasSuffix(f.filename, "_test.go") }
194
195func (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
215type link string
216type 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.
221func (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
229func (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
241argLoop:
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
260var newImporter = func(fset *token.FileSet) types.ImporterFrom {
261	return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
262}
263
264func (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
290func (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
297func (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.
307func (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
326func (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
358func (p *pkg) isMain() bool {
359	for _, f := range p.files {
360		if f.isMain() {
361			return true
362		}
363	}
364	return false
365}
366
367func (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.
379func (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.
432func (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.
463func (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
473const 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.
483func (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
529var (
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.
537var knownNameExceptions = map[string]bool{
538	"LastInsertId": true, // must match database/sql
539	"kWh":          true,
540}
541
542func 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.
556func (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.
700func 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.
770var 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.
814func (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
836var 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).
848func (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).
888func (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
938func (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.
965var 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.
981func (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.
1025func (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.
1056func (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.
1097func (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
1128func 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.
1152func (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".
1185func (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--.
1218func (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.
1246func (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.
1273func (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.
1311func 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.
1329var timeSuffixes = []string{
1330	"Sec", "Secs", "Seconds",
1331	"Msec", "Msecs",
1332	"Milli", "Millis", "Milliseconds",
1333	"Usec", "Usecs", "Microseconds",
1334	"MS", "Ms",
1335}
1336
1337func (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
1373func (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.
1386func (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.
1413func (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.
1433func (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.
1455func 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
1468func (f *file) walk(fn func(ast.Node) bool) {
1469	ast.Walk(walker(fn), f.f)
1470}
1471
1472func (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
1480func (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.
1490type walker func(ast.Node) bool
1491
1492func (w walker) Visit(node ast.Node) ast.Visitor {
1493	if w(node) {
1494		return w
1495	}
1496	return nil
1497}
1498
1499func 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.
1506func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
1507
1508func 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
1513func isOne(expr ast.Expr) bool {
1514	lit, ok := expr.(*ast.BasicLit)
1515	return ok && lit.Kind == token.INT && lit.Value == "1"
1516}
1517
1518func 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
1532var 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.
1544func (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.
1563func (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
1571func (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
1583func (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.
1591func (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.
1605func 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