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}
843
844// lintFuncDoc examines doc comments on functions and methods.
845// It complains if they are missing, or not of the right form.
846// It has specific exclusions for well-known methods (see commonMethods above).
847func (f *file) lintFuncDoc(fn *ast.FuncDecl) {
848	if !ast.IsExported(fn.Name.Name) {
849		// func is unexported
850		return
851	}
852	kind := "function"
853	name := fn.Name.Name
854	if fn.Recv != nil && len(fn.Recv.List) > 0 {
855		// method
856		kind = "method"
857		recv := receiverType(fn)
858		if !ast.IsExported(recv) {
859			// receiver is unexported
860			return
861		}
862		if commonMethods[name] {
863			return
864		}
865		switch name {
866		case "Len", "Less", "Swap":
867			if f.pkg.sortable[recv] {
868				return
869			}
870		}
871		name = recv + "." + name
872	}
873	if fn.Doc == nil {
874		f.errorf(fn, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment or be unexported", kind, name)
875		return
876	}
877	s := fn.Doc.Text()
878	prefix := fn.Name.Name + " "
879	if !strings.HasPrefix(s, prefix) {
880		f.errorf(fn.Doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
881	}
882}
883
884// lintValueSpecDoc examines package-global variables and constants.
885// It complains if they are not individually declared,
886// or if they are not suitably documented in the right form (unless they are in a block that is commented).
887func (f *file) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) {
888	kind := "var"
889	if gd.Tok == token.CONST {
890		kind = "const"
891	}
892
893	if len(vs.Names) > 1 {
894		// Check that none are exported except for the first.
895		for _, n := range vs.Names[1:] {
896			if ast.IsExported(n.Name) {
897				f.errorf(vs, 1, category("comments"), "exported %s %s should have its own declaration", kind, n.Name)
898				return
899			}
900		}
901	}
902
903	// Only one name.
904	name := vs.Names[0].Name
905	if !ast.IsExported(name) {
906		return
907	}
908
909	if vs.Doc == nil && gd.Doc == nil {
910		if genDeclMissingComments[gd] {
911			return
912		}
913		block := ""
914		if kind == "const" && gd.Lparen.IsValid() {
915			block = " (or a comment on this block)"
916		}
917		f.errorf(vs, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment%s or be unexported", kind, name, block)
918		genDeclMissingComments[gd] = true
919		return
920	}
921	// If this GenDecl has parens and a comment, we don't check its comment form.
922	if gd.Lparen.IsValid() && gd.Doc != nil {
923		return
924	}
925	// The relevant text to check will be on either vs.Doc or gd.Doc.
926	// Use vs.Doc preferentially.
927	doc := vs.Doc
928	if doc == nil {
929		doc = gd.Doc
930	}
931	prefix := name + " "
932	if !strings.HasPrefix(doc.Text(), prefix) {
933		f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix)
934	}
935}
936
937func (f *file) checkStutter(id *ast.Ident, thing string) {
938	pkg, name := f.f.Name.Name, id.Name
939	if !ast.IsExported(name) {
940		// unexported name
941		return
942	}
943	// A name stutters if the package name is a strict prefix
944	// and the next character of the name starts a new word.
945	if len(name) <= len(pkg) {
946		// name is too short to stutter.
947		// This permits the name to be the same as the package name.
948		return
949	}
950	if !strings.EqualFold(pkg, name[:len(pkg)]) {
951		return
952	}
953	// We can assume the name is well-formed UTF-8.
954	// If the next rune after the package name is uppercase or an underscore
955	// the it's starting a new word and thus this name stutters.
956	rem := name[len(pkg):]
957	if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) {
958		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)
959	}
960}
961
962// zeroLiteral is a set of ast.BasicLit values that are zero values.
963// It is not exhaustive.
964var zeroLiteral = map[string]bool{
965	"false": true, // bool
966	// runes
967	`'\x00'`: true,
968	`'\000'`: true,
969	// strings
970	`""`: true,
971	"``": true,
972	// numerics
973	"0":   true,
974	"0.":  true,
975	"0.0": true,
976	"0i":  true,
977}
978
979// lintElses examines else blocks. It complains about any else block whose if block ends in a return.
980func (f *file) lintElses() {
981	// We don't want to flag if { } else if { } else { } constructions.
982	// They will appear as an IfStmt whose Else field is also an IfStmt.
983	// Record such a node so we ignore it when we visit it.
984	ignore := make(map[*ast.IfStmt]bool)
985
986	f.walk(func(node ast.Node) bool {
987		ifStmt, ok := node.(*ast.IfStmt)
988		if !ok || ifStmt.Else == nil {
989			return true
990		}
991		if elseif, ok := ifStmt.Else.(*ast.IfStmt); ok {
992			ignore[elseif] = true
993			return true
994		}
995		if ignore[ifStmt] {
996			return true
997		}
998		if _, ok := ifStmt.Else.(*ast.BlockStmt); !ok {
999			// only care about elses without conditions
1000			return true
1001		}
1002		if len(ifStmt.Body.List) == 0 {
1003			return true
1004		}
1005		shortDecl := false // does the if statement have a ":=" initialization statement?
1006		if ifStmt.Init != nil {
1007			if as, ok := ifStmt.Init.(*ast.AssignStmt); ok && as.Tok == token.DEFINE {
1008				shortDecl = true
1009			}
1010		}
1011		lastStmt := ifStmt.Body.List[len(ifStmt.Body.List)-1]
1012		if _, ok := lastStmt.(*ast.ReturnStmt); ok {
1013			extra := ""
1014			if shortDecl {
1015				extra = " (move short variable declaration to its own line if necessary)"
1016			}
1017			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)
1018		}
1019		return true
1020	})
1021}
1022
1023// lintRanges examines range clauses. It complains about redundant constructions.
1024func (f *file) lintRanges() {
1025	f.walk(func(node ast.Node) bool {
1026		rs, ok := node.(*ast.RangeStmt)
1027		if !ok {
1028			return true
1029		}
1030
1031		if isIdent(rs.Key, "_") && (rs.Value == nil || isIdent(rs.Value, "_")) {
1032			p := f.errorf(rs.Key, 1, category("range-loop"), "should omit values from range; this loop is equivalent to `for range ...`")
1033
1034			newRS := *rs // shallow copy
1035			newRS.Value = nil
1036			newRS.Key = nil
1037			p.ReplacementLine = f.firstLineOf(&newRS, rs)
1038
1039			return true
1040		}
1041
1042		if isIdent(rs.Value, "_") {
1043			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)
1044
1045			newRS := *rs // shallow copy
1046			newRS.Value = nil
1047			p.ReplacementLine = f.firstLineOf(&newRS, rs)
1048		}
1049
1050		return true
1051	})
1052}
1053
1054// lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation.
1055func (f *file) lintErrorf() {
1056	f.walk(func(node ast.Node) bool {
1057		ce, ok := node.(*ast.CallExpr)
1058		if !ok || len(ce.Args) != 1 {
1059			return true
1060		}
1061		isErrorsNew := isPkgDot(ce.Fun, "errors", "New")
1062		var isTestingError bool
1063		se, ok := ce.Fun.(*ast.SelectorExpr)
1064		if ok && se.Sel.Name == "Error" {
1065			if typ := f.pkg.typeOf(se.X); typ != nil {
1066				isTestingError = typ.String() == "*testing.T"
1067			}
1068		}
1069		if !isErrorsNew && !isTestingError {
1070			return true
1071		}
1072		if !f.imports("errors") {
1073			return true
1074		}
1075		arg := ce.Args[0]
1076		ce, ok = arg.(*ast.CallExpr)
1077		if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
1078			return true
1079		}
1080		errorfPrefix := "fmt"
1081		if isTestingError {
1082			errorfPrefix = f.render(se.X)
1083		}
1084		p := f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix)
1085
1086		m := f.srcLineWithMatch(ce, `^(.*)`+f.render(se)+`\(fmt\.Sprintf\((.*)\)\)(.*)$`)
1087		if m != nil {
1088			p.ReplacementLine = m[1] + errorfPrefix + ".Errorf(" + m[2] + ")" + m[3]
1089		}
1090
1091		return true
1092	})
1093}
1094
1095// lintErrors examines global error vars. It complains if they aren't named in the standard way.
1096func (f *file) lintErrors() {
1097	for _, decl := range f.f.Decls {
1098		gd, ok := decl.(*ast.GenDecl)
1099		if !ok || gd.Tok != token.VAR {
1100			continue
1101		}
1102		for _, spec := range gd.Specs {
1103			spec := spec.(*ast.ValueSpec)
1104			if len(spec.Names) != 1 || len(spec.Values) != 1 {
1105				continue
1106			}
1107			ce, ok := spec.Values[0].(*ast.CallExpr)
1108			if !ok {
1109				continue
1110			}
1111			if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
1112				continue
1113			}
1114
1115			id := spec.Names[0]
1116			prefix := "err"
1117			if id.IsExported() {
1118				prefix = "Err"
1119			}
1120			if !strings.HasPrefix(id.Name, prefix) {
1121				f.errorf(id, 0.9, category("naming"), "error var %s should have name of the form %sFoo", id.Name, prefix)
1122			}
1123		}
1124	}
1125}
1126
1127func lintErrorString(s string) (isClean bool, conf float64) {
1128	const basicConfidence = 0.8
1129	const capConfidence = basicConfidence - 0.2
1130	first, firstN := utf8.DecodeRuneInString(s)
1131	last, _ := utf8.DecodeLastRuneInString(s)
1132	if last == '.' || last == ':' || last == '!' || last == '\n' {
1133		return false, basicConfidence
1134	}
1135	if unicode.IsUpper(first) {
1136		// People use proper nouns and exported Go identifiers in error strings,
1137		// so decrease the confidence of warnings for capitalization.
1138		if len(s) <= firstN {
1139			return false, capConfidence
1140		}
1141		// Flag strings starting with something that doesn't look like an initialism.
1142		if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) {
1143			return false, capConfidence
1144		}
1145	}
1146	return true, 0
1147}
1148
1149// lintErrorStrings examines error strings.
1150// It complains if they are capitalized or end in punctuation or a newline.
1151func (f *file) lintErrorStrings() {
1152	f.walk(func(node ast.Node) bool {
1153		ce, ok := node.(*ast.CallExpr)
1154		if !ok {
1155			return true
1156		}
1157		if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") {
1158			return true
1159		}
1160		if len(ce.Args) < 1 {
1161			return true
1162		}
1163		str, ok := ce.Args[0].(*ast.BasicLit)
1164		if !ok || str.Kind != token.STRING {
1165			return true
1166		}
1167		s, _ := strconv.Unquote(str.Value) // can assume well-formed Go
1168		if s == "" {
1169			return true
1170		}
1171		clean, conf := lintErrorString(s)
1172		if clean {
1173			return true
1174		}
1175
1176		f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"),
1177			"error strings should not be capitalized or end with punctuation or a newline")
1178		return true
1179	})
1180}
1181
1182// lintReceiverNames examines receiver names. It complains about inconsistent
1183// names used for the same type and names such as "this".
1184func (f *file) lintReceiverNames() {
1185	typeReceiver := map[string]string{}
1186	f.walk(func(n ast.Node) bool {
1187		fn, ok := n.(*ast.FuncDecl)
1188		if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
1189			return true
1190		}
1191		names := fn.Recv.List[0].Names
1192		if len(names) < 1 {
1193			return true
1194		}
1195		name := names[0].Name
1196		const ref = styleGuideBase + "#receiver-names"
1197		if name == "_" {
1198			f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore, omit the name if it is unused`)
1199			return true
1200		}
1201		if name == "this" || name == "self" {
1202			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"`)
1203			return true
1204		}
1205		recv := receiverType(fn)
1206		if prev, ok := typeReceiver[recv]; ok && prev != name {
1207			f.errorf(n, 1, link(ref), category("naming"), "receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv)
1208			return true
1209		}
1210		typeReceiver[recv] = name
1211		return true
1212	})
1213}
1214
1215// lintIncDec examines statements that increment or decrement a variable.
1216// It complains if they don't use x++ or x--.
1217func (f *file) lintIncDec() {
1218	f.walk(func(n ast.Node) bool {
1219		as, ok := n.(*ast.AssignStmt)
1220		if !ok {
1221			return true
1222		}
1223		if len(as.Lhs) != 1 {
1224			return true
1225		}
1226		if !isOne(as.Rhs[0]) {
1227			return true
1228		}
1229		var suffix string
1230		switch as.Tok {
1231		case token.ADD_ASSIGN:
1232			suffix = "++"
1233		case token.SUB_ASSIGN:
1234			suffix = "--"
1235		default:
1236			return true
1237		}
1238		f.errorf(as, 0.8, category("unary-op"), "should replace %s with %s%s", f.render(as), f.render(as.Lhs[0]), suffix)
1239		return true
1240	})
1241}
1242
1243// lintErrorReturn examines function declarations that return an error.
1244// It complains if the error isn't the last parameter.
1245func (f *file) lintErrorReturn() {
1246	f.walk(func(n ast.Node) bool {
1247		fn, ok := n.(*ast.FuncDecl)
1248		if !ok || fn.Type.Results == nil {
1249			return true
1250		}
1251		ret := fn.Type.Results.List
1252		if len(ret) <= 1 {
1253			return true
1254		}
1255		if isIdent(ret[len(ret)-1].Type, "error") {
1256			return true
1257		}
1258		// An error return parameter should be the last parameter.
1259		// Flag any error parameters found before the last.
1260		for _, r := range ret[:len(ret)-1] {
1261			if isIdent(r.Type, "error") {
1262				f.errorf(fn, 0.9, category("arg-order"), "error should be the last type when returning multiple items")
1263				break // only flag one
1264			}
1265		}
1266		return true
1267	})
1268}
1269
1270// lintUnexportedReturn examines exported function declarations.
1271// It complains if any return an unexported type.
1272func (f *file) lintUnexportedReturn() {
1273	f.walk(func(n ast.Node) bool {
1274		fn, ok := n.(*ast.FuncDecl)
1275		if !ok {
1276			return true
1277		}
1278		if fn.Type.Results == nil {
1279			return false
1280		}
1281		if !fn.Name.IsExported() {
1282			return false
1283		}
1284		thing := "func"
1285		if fn.Recv != nil && len(fn.Recv.List) > 0 {
1286			thing = "method"
1287			if !ast.IsExported(receiverType(fn)) {
1288				// Don't report exported methods of unexported types,
1289				// such as private implementations of sort.Interface.
1290				return false
1291			}
1292		}
1293		for _, ret := range fn.Type.Results.List {
1294			typ := f.pkg.typeOf(ret.Type)
1295			if exportedType(typ) {
1296				continue
1297			}
1298			f.errorf(ret.Type, 0.8, category("unexported-type-in-api"),
1299				"exported %s %s returns unexported type %s, which can be annoying to use",
1300				thing, fn.Name.Name, typ)
1301			break // only flag one
1302		}
1303		return false
1304	})
1305}
1306
1307// exportedType reports whether typ is an exported type.
1308// It is imprecise, and will err on the side of returning true,
1309// such as for composite types.
1310func exportedType(typ types.Type) bool {
1311	switch T := typ.(type) {
1312	case *types.Named:
1313		// Builtin types have no package.
1314		return T.Obj().Pkg() == nil || T.Obj().Exported()
1315	case *types.Map:
1316		return exportedType(T.Key()) && exportedType(T.Elem())
1317	case interface {
1318		Elem() types.Type
1319	}: // array, slice, pointer, chan
1320		return exportedType(T.Elem())
1321	}
1322	// Be conservative about other types, such as struct, interface, etc.
1323	return true
1324}
1325
1326// timeSuffixes is a list of name suffixes that imply a time unit.
1327// This is not an exhaustive list.
1328var timeSuffixes = []string{
1329	"Sec", "Secs", "Seconds",
1330	"Msec", "Msecs",
1331	"Milli", "Millis", "Milliseconds",
1332	"Usec", "Usecs", "Microseconds",
1333	"MS", "Ms",
1334}
1335
1336func (f *file) lintTimeNames() {
1337	f.walk(func(node ast.Node) bool {
1338		v, ok := node.(*ast.ValueSpec)
1339		if !ok {
1340			return true
1341		}
1342		for _, name := range v.Names {
1343			origTyp := f.pkg.typeOf(name)
1344			// Look for time.Duration or *time.Duration;
1345			// the latter is common when using flag.Duration.
1346			typ := origTyp
1347			if pt, ok := typ.(*types.Pointer); ok {
1348				typ = pt.Elem()
1349			}
1350			if !f.pkg.isNamedType(typ, "time", "Duration") {
1351				continue
1352			}
1353			suffix := ""
1354			for _, suf := range timeSuffixes {
1355				if strings.HasSuffix(name.Name, suf) {
1356					suffix = suf
1357					break
1358				}
1359			}
1360			if suffix == "" {
1361				continue
1362			}
1363			f.errorf(v, 0.9, category("time"), "var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix)
1364		}
1365		return true
1366	})
1367}
1368
1369// lintContextKeyTypes checks for call expressions to context.WithValue with
1370// basic types used for the key argument.
1371// See: https://golang.org/issue/17293
1372func (f *file) lintContextKeyTypes() {
1373	f.walk(func(node ast.Node) bool {
1374		switch node := node.(type) {
1375		case *ast.CallExpr:
1376			f.checkContextKeyType(node)
1377		}
1378
1379		return true
1380	})
1381}
1382
1383// checkContextKeyType reports an error if the call expression calls
1384// context.WithValue with a key argument of basic type.
1385func (f *file) checkContextKeyType(x *ast.CallExpr) {
1386	sel, ok := x.Fun.(*ast.SelectorExpr)
1387	if !ok {
1388		return
1389	}
1390	pkg, ok := sel.X.(*ast.Ident)
1391	if !ok || pkg.Name != "context" {
1392		return
1393	}
1394	if sel.Sel.Name != "WithValue" {
1395		return
1396	}
1397
1398	// key is second argument to context.WithValue
1399	if len(x.Args) != 3 {
1400		return
1401	}
1402	key := f.pkg.typesInfo.Types[x.Args[1]]
1403
1404	if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid {
1405		f.errorf(x, 1.0, category("context"), fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type))
1406	}
1407}
1408
1409// lintContextArgs examines function declarations that contain an
1410// argument with a type of context.Context
1411// It complains if that argument isn't the first parameter.
1412func (f *file) lintContextArgs() {
1413	f.walk(func(n ast.Node) bool {
1414		fn, ok := n.(*ast.FuncDecl)
1415		if !ok || len(fn.Type.Params.List) <= 1 {
1416			return true
1417		}
1418		// A context.Context should be the first parameter of a function.
1419		// Flag any that show up after the first.
1420		for _, arg := range fn.Type.Params.List[1:] {
1421			if isPkgDot(arg.Type, "context", "Context") {
1422				f.errorf(fn, 0.9, link("https://golang.org/pkg/context/"), category("arg-order"), "context.Context should be the first parameter of a function")
1423				break // only flag one
1424			}
1425		}
1426		return true
1427	})
1428}
1429
1430// containsComments returns whether the interval [start, end) contains any
1431// comments without "// MATCH " prefix.
1432func (f *file) containsComments(start, end token.Pos) bool {
1433	for _, cgroup := range f.f.Comments {
1434		comments := cgroup.List
1435		if comments[0].Slash >= end {
1436			// All comments starting with this group are after end pos.
1437			return false
1438		}
1439		if comments[len(comments)-1].Slash < start {
1440			// Comments group ends before start pos.
1441			continue
1442		}
1443		for _, c := range comments {
1444			if start <= c.Slash && c.Slash < end && !strings.HasPrefix(c.Text, "// MATCH ") {
1445				return true
1446			}
1447		}
1448	}
1449	return false
1450}
1451
1452// receiverType returns the named type of the method receiver, sans "*",
1453// or "invalid-type" if fn.Recv is ill formed.
1454func receiverType(fn *ast.FuncDecl) string {
1455	switch e := fn.Recv.List[0].Type.(type) {
1456	case *ast.Ident:
1457		return e.Name
1458	case *ast.StarExpr:
1459		if id, ok := e.X.(*ast.Ident); ok {
1460			return id.Name
1461		}
1462	}
1463	// The parser accepts much more than just the legal forms.
1464	return "invalid-type"
1465}
1466
1467func (f *file) walk(fn func(ast.Node) bool) {
1468	ast.Walk(walker(fn), f.f)
1469}
1470
1471func (f *file) render(x interface{}) string {
1472	var buf bytes.Buffer
1473	if err := printer.Fprint(&buf, f.fset, x); err != nil {
1474		panic(err)
1475	}
1476	return buf.String()
1477}
1478
1479func (f *file) debugRender(x interface{}) string {
1480	var buf bytes.Buffer
1481	if err := ast.Fprint(&buf, f.fset, x, nil); err != nil {
1482		panic(err)
1483	}
1484	return buf.String()
1485}
1486
1487// walker adapts a function to satisfy the ast.Visitor interface.
1488// The function return whether the walk should proceed into the node's children.
1489type walker func(ast.Node) bool
1490
1491func (w walker) Visit(node ast.Node) ast.Visitor {
1492	if w(node) {
1493		return w
1494	}
1495	return nil
1496}
1497
1498func isIdent(expr ast.Expr, ident string) bool {
1499	id, ok := expr.(*ast.Ident)
1500	return ok && id.Name == ident
1501}
1502
1503// isBlank returns whether id is the blank identifier "_".
1504// If id == nil, the answer is false.
1505func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" }
1506
1507func isPkgDot(expr ast.Expr, pkg, name string) bool {
1508	sel, ok := expr.(*ast.SelectorExpr)
1509	return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
1510}
1511
1512func isOne(expr ast.Expr) bool {
1513	lit, ok := expr.(*ast.BasicLit)
1514	return ok && lit.Kind == token.INT && lit.Value == "1"
1515}
1516
1517func isCgoExported(f *ast.FuncDecl) bool {
1518	if f.Recv != nil || f.Doc == nil {
1519		return false
1520	}
1521
1522	cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
1523	for _, c := range f.Doc.List {
1524		if cgoExport.MatchString(c.Text) {
1525			return true
1526		}
1527	}
1528	return false
1529}
1530
1531var basicTypeKinds = map[types.BasicKind]string{
1532	types.UntypedBool:    "bool",
1533	types.UntypedInt:     "int",
1534	types.UntypedRune:    "rune",
1535	types.UntypedFloat:   "float64",
1536	types.UntypedComplex: "complex128",
1537	types.UntypedString:  "string",
1538}
1539
1540// isUntypedConst reports whether expr is an untyped constant,
1541// and indicates what its default type is.
1542// scope may be nil.
1543func (f *file) isUntypedConst(expr ast.Expr) (defType string, ok bool) {
1544	// Re-evaluate expr outside of its context to see if it's untyped.
1545	// (An expr evaluated within, for example, an assignment context will get the type of the LHS.)
1546	exprStr := f.render(expr)
1547	tv, err := types.Eval(f.fset, f.pkg.typesPkg, expr.Pos(), exprStr)
1548	if err != nil {
1549		return "", false
1550	}
1551	if b, ok := tv.Type.(*types.Basic); ok {
1552		if dt, ok := basicTypeKinds[b.Kind()]; ok {
1553			return dt, true
1554		}
1555	}
1556
1557	return "", false
1558}
1559
1560// firstLineOf renders the given node and returns its first line.
1561// It will also match the indentation of another node.
1562func (f *file) firstLineOf(node, match ast.Node) string {
1563	line := f.render(node)
1564	if i := strings.Index(line, "\n"); i >= 0 {
1565		line = line[:i]
1566	}
1567	return f.indentOf(match) + line
1568}
1569
1570func (f *file) indentOf(node ast.Node) string {
1571	line := srcLine(f.src, f.fset.Position(node.Pos()))
1572	for i, r := range line {
1573		switch r {
1574		case ' ', '\t':
1575		default:
1576			return line[:i]
1577		}
1578	}
1579	return line // unusual or empty line
1580}
1581
1582func (f *file) srcLineWithMatch(node ast.Node, pattern string) (m []string) {
1583	line := srcLine(f.src, f.fset.Position(node.Pos()))
1584	line = strings.TrimSuffix(line, "\n")
1585	rx := regexp.MustCompile(pattern)
1586	return rx.FindStringSubmatch(line)
1587}
1588
1589// imports returns true if the current file imports the specified package path.
1590func (f *file) imports(importPath string) bool {
1591	all := astutil.Imports(f.fset, f.f)
1592	for _, p := range all {
1593		for _, i := range p {
1594			uq, err := strconv.Unquote(i.Path.Value)
1595			if err == nil && importPath == uq {
1596				return true
1597			}
1598		}
1599	}
1600	return false
1601}
1602
1603// srcLine returns the complete line at p, including the terminating newline.
1604func srcLine(src []byte, p token.Position) string {
1605	// Run to end of line in both directions if not at line start/end.
1606	lo, hi := p.Offset, p.Offset+1
1607	for lo > 0 && src[lo-1] != '\n' {
1608		lo--
1609	}
1610	for hi < len(src) && src[hi-1] != '\n' {
1611		hi++
1612	}
1613	return string(src[lo:hi])
1614}
1615