Codebase list golint / 0ec28b4
New upstream version 0.0+git20200130.910be7a Anthony Fok 4 years ago
20 changed file(s) with 456 addition(s) and 311 deletion(s). Raw diff Collapse all Expand all
0 sudo: false
01 language: go
12 go:
2 - 1.6
3 - 1.7
3 - 1.10.x
4 - 1.11.x
5 - master
6
7 go_import_path: golang.org/x/lint
48
59 install:
610 - go get -t -v ./...
711
812 script:
9 - go test -v ./...
13 - go test -v -race ./...
14
15 matrix:
16 allow_failures:
17 - go: master
18 fast_finish: true
55
66 Check you have the latest version of its dependencies. Run
77 ```
8 go get -u github.com/golang/lint
8 go get -u golang.org/x/lint/golint
99 ```
1010 If you still have problems, consider searching for existing issues before filing a new issue.
1111
33
44 ## Installation
55
6 Golint requires Go 1.6 or later.
6 Golint requires a
7 [supported release of Go](https://golang.org/doc/devel/release.html#policy).
78
8 go get -u github.com/golang/lint/golint
9 go get -u golang.org/x/lint/golint
10
11 To find out where `golint` was installed you can run `go list -f {{.Target}} golang.org/x/lint/golint`. For `golint` to be used globally add that directory to the `$PATH` environment setting.
912
1013 ## Usage
1114
12 Invoke `golint` with one or more filenames, a directory, or a package named
15 Invoke `golint` with one or more filenames, directories, or packages named
1316 by its import path. Golint uses the same
1417 [import path syntax](https://golang.org/cmd/go/#hdr-Import_path_syntax) as
1518 the `go` command and therefore
4043 [Effective Go](https://golang.org/doc/effective_go.html) and the
4144 [CodeReviewComments wiki page](https://golang.org/wiki/CodeReviewComments).
4245
43 If you find an established style that is frequently violated, and which
44 you think golint could statically check,
45 [file an issue](https://github.com/golang/lint/issues).
46 ## Scope
47
48 Golint is meant to carry out the stylistic conventions put forth in
49 [Effective Go](https://golang.org/doc/effective_go.html) and
50 [CodeReviewComments](https://golang.org/wiki/CodeReviewComments).
51 Changes that are not aligned with those documents will not be considered.
4652
4753 ## Contributions
4854
49 Contributions to this project are welcome, though please send mail before
50 starting work on anything major. Contributors retain their copyright, so we
51 need you to fill out
55 Contributions to this project are welcome provided they are [in scope](#scope),
56 though please send mail before starting work on anything major.
57 Contributors retain their copyright, so we need you to fill out
5258 [a short form](https://developers.google.com/open-source/cla/individual)
5359 before we can accept your contribution.
5460
5662
5763 Add this to your ~/.vimrc:
5864
59 set rtp+=$GOPATH/src/github.com/golang/lint/misc/vim
65 set rtp+=$GOPATH/src/golang.org/x/lint/misc/vim
6066
6167 If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value.
6268
7177
7278 Add this to your `.emacs` file:
7379
74 (add-to-list 'load-path (concat (getenv "GOPATH") "/src/github.com/golang/lint/misc/emacs"))
80 (add-to-list 'load-path (concat (getenv "GOPATH") "/src/golang.org/x/lint/misc/emacs/"))
7581 (require 'golint)
7682
7783 If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value.
0 module golang.org/x/lint
1
2 go 1.11
3
4 require golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7
0 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
2 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
3 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
4 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
5 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
6 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
7 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
8 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
9 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
10 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
11 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1515 "path/filepath"
1616 "strings"
1717
18 "github.com/golang/lint"
18 "golang.org/x/lint"
1919 )
2020
2121 var (
2727 func usage() {
2828 fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
2929 fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
30 fmt.Fprintf(os.Stderr, "\tgolint [flags] package\n")
31 fmt.Fprintf(os.Stderr, "\tgolint [flags] directory\n")
32 fmt.Fprintf(os.Stderr, "\tgolint [flags] files... # must be a single package\n")
30 fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n")
31 fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n")
32 fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n")
3333 fmt.Fprintf(os.Stderr, "Flags:\n")
3434 flag.PrintDefaults()
3535 }
3838 flag.Usage = usage
3939 flag.Parse()
4040
41 switch flag.NArg() {
42 case 0:
41 if flag.NArg() == 0 {
4342 lintDir(".")
44 case 1:
45 arg := flag.Arg(0)
46 if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-4]) {
47 for _, dirname := range allPackagesInFS(arg) {
48 lintDir(dirname)
49 }
50 } else if isDir(arg) {
51 lintDir(arg)
52 } else if exists(arg) {
53 lintFiles(arg)
54 } else {
55 for _, pkgname := range importPaths([]string{arg}) {
56 lintPackage(pkgname)
43 } else {
44 // dirsRun, filesRun, and pkgsRun indicate whether golint is applied to
45 // directory, file or package targets. The distinction affects which
46 // checks are run. It is no valid to mix target types.
47 var dirsRun, filesRun, pkgsRun int
48 var args []string
49 for _, arg := range flag.Args() {
50 if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
51 dirsRun = 1
52 for _, dirname := range allPackagesInFS(arg) {
53 args = append(args, dirname)
54 }
55 } else if isDir(arg) {
56 dirsRun = 1
57 args = append(args, arg)
58 } else if exists(arg) {
59 filesRun = 1
60 args = append(args, arg)
61 } else {
62 pkgsRun = 1
63 args = append(args, arg)
5764 }
5865 }
59 default:
60 lintFiles(flag.Args()...)
66
67 if dirsRun+filesRun+pkgsRun != 1 {
68 usage()
69 os.Exit(2)
70 }
71 switch {
72 case dirsRun == 1:
73 for _, dir := range args {
74 lintDir(dir)
75 }
76 case filesRun == 1:
77 lintFiles(args...)
78 case pkgsRun == 1:
79 for _, pkg := range importPaths(args) {
80 lintPackage(pkg)
81 }
82 }
6183 }
6284
6385 if *setExitStatus && suggestions > 0 {
2121 "strings"
2222 )
2323
24 var buildContext = build.Default
25
2624 var (
27 goroot = filepath.Clean(runtime.GOROOT())
28 gorootSrc = filepath.Join(goroot, "src")
25 buildContext = build.Default
26 goroot = filepath.Clean(runtime.GOROOT())
27 gorootSrc = filepath.Join(goroot, "src")
2928 )
3029
3130 // importPathsNoDotExpansion returns the import paths to use for the given
0 // Copyright (c) 2018 The Go Authors. All rights reserved.
1 //
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file or at
4 // https://developers.google.com/open-source/licenses/bsd.
5
6 // +build go1.12
7
8 // Require use of the correct import path only for Go 1.12+ users, so
9 // any breakages coincide with people updating their CI configs or
10 // whatnot.
11
12 package main // import "golang.org/x/lint/golint"
+183
-169
lint.go less more
44 // https://developers.google.com/open-source/licenses/bsd.
55
66 // Package lint contains a linter for Go source code.
7 package lint
7 package lint // import "golang.org/x/lint"
88
99 import (
10 "bufio"
1011 "bytes"
1112 "fmt"
1213 "go/ast"
2122 "unicode"
2223 "unicode/utf8"
2324
24 "golang.org/x/tools/go/gcimporter15"
25 "golang.org/x/tools/go/ast/astutil"
26 "golang.org/x/tools/go/gcexportdata"
2527 )
2628
2729 const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
8082 // LintFiles lints a set of files of a single package.
8183 // The argument is a map of filename to source.
8284 func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) {
83 if len(files) == 0 {
84 return nil, nil
85 }
8685 pkg := &pkg{
8786 fset: token.NewFileSet(),
8887 files: make(map[string]*file),
8988 }
9089 var pkgName string
9190 for filename, src := range files {
91 if isGenerated(src) {
92 continue // See issue #239
93 }
9294 f, err := parser.ParseFile(pkg.fset, filename, src, parser.ParseComments)
9395 if err != nil {
9496 return nil, err
106108 filename: filename,
107109 }
108110 }
111 if len(pkg.files) == 0 {
112 return nil, nil
113 }
109114 return pkg.lint(), nil
115 }
116
117 var (
118 genHdr = []byte("// Code generated ")
119 genFtr = []byte(" DO NOT EDIT.")
120 )
121
122 // isGenerated reports whether the source file is generated code
123 // according the rules from https://golang.org/s/generatedcode.
124 func isGenerated(src []byte) bool {
125 sc := bufio.NewScanner(bytes.NewReader(src))
126 for sc.Scan() {
127 b := sc.Bytes()
128 if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
129 return true
130 }
131 }
132 return false
110133 }
111134
112135 // pkg represents a package being linted.
174197 f.lintBlankImports()
175198 f.lintExported()
176199 f.lintNames()
177 f.lintVarDecls()
178200 f.lintElses()
179201 f.lintRanges()
180202 f.lintErrorf()
234256 return &p.problems[len(p.problems)-1]
235257 }
236258
237 var gcImporter = gcimporter.Import
238
239 // importer implements go/types.Importer{,From}.
240 type importer struct {
241 impFn func(packages map[string]*types.Package, path, srcDir string) (*types.Package, error)
242 packages map[string]*types.Package
243 }
244
245 func (i importer) Import(path string) (*types.Package, error) {
246 return i.impFn(i.packages, path, "")
247 }
248
249 func (i importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
250 return i.impFn(i.packages, path, srcDir)
259 var newImporter = func(fset *token.FileSet) types.ImporterFrom {
260 return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
251261 }
252262
253263 func (p *pkg) typeCheck() error {
254264 config := &types.Config{
255265 // By setting a no-op error reporter, the type checker does as much work as possible.
256 Error: func(error) {},
257 Importer: importer{
258 impFn: gcImporter,
259 packages: make(map[string]*types.Package),
260 },
266 Error: func(error) {},
267 Importer: newImporter(p.fset),
261268 }
262269 info := &types.Info{
263270 Types: make(map[ast.Expr]types.TypeAndValue),
453460
454461 // lintImports examines import blocks.
455462 func (f *file) lintImports() {
456
457463 for i, is := range f.f.Imports {
458464 _ = i
459465 if is.Name != nil && is.Name.Name == "." && !f.isTest() {
461467 }
462468
463469 }
464
465470 }
466471
467472 const docCommentsLink = styleGuideBase + "#doc-comments"
520525 })
521526 }
522527
523 var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
528 var (
529 allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`)
530 anyCapsRE = regexp.MustCompile(`[A-Z]`)
531 )
524532
525533 // knownNameExceptions is a set of names that are known to be exempt from naming checks.
526534 // This is usually because they are constrained by having to match names in the
530538 "kWh": true,
531539 }
532540
541 func isInTopLevel(f *ast.File, ident *ast.Ident) bool {
542 path, _ := astutil.PathEnclosingInterval(f, ident.Pos(), ident.End())
543 for _, f := range path {
544 switch f.(type) {
545 case *ast.File, *ast.GenDecl, *ast.ValueSpec, *ast.Ident:
546 continue
547 }
548 return false
549 }
550 return true
551 }
552
533553 // lintNames examines all names in the file.
534554 // It complains if any use underscores or incorrect known initialisms.
535555 func (f *file) lintNames() {
537557 if strings.Contains(f.f.Name.Name, "_") && !strings.HasSuffix(f.f.Name.Name, "_test") {
538558 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")
539559 }
560 if anyCapsRE.MatchString(f.f.Name.Name) {
561 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))
562 }
540563
541564 check := func(id *ast.Ident, thing string) {
542565 if id.Name == "_" {
548571
549572 // Handle two common styles from other languages that don't belong in Go.
550573 if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") {
551 f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use ALL_CAPS in Go names; use CamelCase")
552 return
553 }
554 if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
555 should := string(id.Name[1]+'a'-'A') + id.Name[2:]
556 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)
574 capCount := 0
575 for _, c := range id.Name {
576 if 'A' <= c && c <= 'Z' {
577 capCount++
578 }
579 }
580 if capCount >= 2 {
581 f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use ALL_CAPS in Go names; use CamelCase")
582 return
583 }
584 }
585 if thing == "const" || (thing == "var" && isInTopLevel(f.f, id)) {
586 if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
587 should := string(id.Name[1]+'a'-'A') + id.Name[2:]
588 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)
589 }
557590 }
558591
559592 should := lintName(id.Name)
560593 if id.Name == should {
561594 return
562595 }
596
563597 if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") {
564598 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)
565599 return
597631 thing = "method"
598632 }
599633
600 check(v.Name, thing)
634 // Exclude naming warnings for functions that are exported to C but
635 // not exported in the Go API.
636 // See https://github.com/golang/lint/issues/144.
637 if ast.IsExported(v.Name.Name) || !isCgoExported(v) {
638 check(v.Name, thing)
639 }
601640
602641 checkList(v.Type.Params, thing+" parameter")
603642 checkList(v.Type.Results, thing+" result")
936975 "0i": true,
937976 }
938977
939 // lintVarDecls examines variable declarations. It complains about declarations with
940 // redundant LHS types that can be inferred from the RHS.
941 func (f *file) lintVarDecls() {
942 var lastGen *ast.GenDecl // last GenDecl entered.
943
944 f.walk(func(node ast.Node) bool {
945 switch v := node.(type) {
946 case *ast.GenDecl:
947 if v.Tok != token.CONST && v.Tok != token.VAR {
948 return false
949 }
950 lastGen = v
951 return true
952 case *ast.ValueSpec:
953 if lastGen.Tok == token.CONST {
954 return false
955 }
956 if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 {
957 return false
958 }
959 rhs := v.Values[0]
960 // An underscore var appears in a common idiom for compile-time interface satisfaction,
961 // as in "var _ Interface = (*Concrete)(nil)".
962 if isIdent(v.Names[0], "_") {
963 return false
964 }
965 // If the RHS is a zero value, suggest dropping it.
966 zero := false
967 if lit, ok := rhs.(*ast.BasicLit); ok {
968 zero = zeroLiteral[lit.Value]
969 } else if isIdent(rhs, "nil") {
970 zero = true
971 }
972 if zero {
973 f.errorf(rhs, 0.9, category("zero-value"), "should drop = %s from declaration of var %s; it is the zero value", f.render(rhs), v.Names[0])
974 return false
975 }
976 lhsTyp := f.pkg.typeOf(v.Type)
977 rhsTyp := f.pkg.typeOf(rhs)
978
979 if !validType(lhsTyp) || !validType(rhsTyp) {
980 // Type checking failed (often due to missing imports).
981 return false
982 }
983
984 if !types.Identical(lhsTyp, rhsTyp) {
985 // Assignment to a different type is not redundant.
986 return false
987 }
988
989 // The next three conditions are for suppressing the warning in situations
990 // where we were unable to typecheck.
991
992 // If the LHS type is an interface, don't warn, since it is probably a
993 // concrete type on the RHS. Note that our feeble lexical check here
994 // will only pick up interface{} and other literal interface types;
995 // that covers most of the cases we care to exclude right now.
996 if _, ok := v.Type.(*ast.InterfaceType); ok {
997 return false
998 }
999 // If the RHS is an untyped const, only warn if the LHS type is its default type.
1000 if defType, ok := f.isUntypedConst(rhs); ok && !isIdent(v.Type, defType) {
1001 return false
1002 }
1003
1004 f.errorf(v.Type, 0.8, category("type-inference"), "should omit type %s from declaration of var %s; it will be inferred from the right-hand side", f.render(v.Type), v.Names[0])
1005 return false
1006 }
1007 return true
1008 })
1009 }
1010
1011 func validType(T types.Type) bool {
1012 return T != nil &&
1013 T != types.Typ[types.Invalid] &&
1014 !strings.Contains(T.String(), "invalid type") // good but not foolproof
1015 }
1016
1017978 // lintElses examines else blocks. It complains about any else block whose if block ends in a return.
1018979 func (f *file) lintElses() {
1019980 // We don't want to flag if { } else if { } else { } constructions.
1026987 if !ok || ifStmt.Else == nil {
1027988 return true
1028989 }
1029 if ignore[ifStmt] {
1030 return true
1031 }
1032990 if elseif, ok := ifStmt.Else.(*ast.IfStmt); ok {
1033991 ignore[elseif] = true
992 return true
993 }
994 if ignore[ifStmt] {
1034995 return true
1035996 }
1036997 if _, ok := ifStmt.Else.(*ast.BlockStmt); !ok {
10651026 if !ok {
10661027 return true
10671028 }
1068 if rs.Value == nil {
1069 // for x = range m { ... }
1070 return true // single var form
1071 }
1072 if !isIdent(rs.Value, "_") {
1073 // for ?, y = range m { ... }
1074 return true
1075 }
1076
1077 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)
1078
1079 newRS := *rs // shallow copy
1080 newRS.Value = nil
1081 p.ReplacementLine = f.firstLineOf(&newRS, rs)
1029
1030 if isIdent(rs.Key, "_") && (rs.Value == nil || isIdent(rs.Value, "_")) {
1031 p := f.errorf(rs.Key, 1, category("range-loop"), "should omit values from range; this loop is equivalent to `for range ...`")
1032
1033 newRS := *rs // shallow copy
1034 newRS.Value = nil
1035 newRS.Key = nil
1036 p.ReplacementLine = f.firstLineOf(&newRS, rs)
1037
1038 return true
1039 }
1040
1041 if isIdent(rs.Value, "_") {
1042 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)
1043
1044 newRS := *rs // shallow copy
1045 newRS.Value = nil
1046 p.ReplacementLine = f.firstLineOf(&newRS, rs)
1047 }
10821048
10831049 return true
10841050 })
11021068 if !isErrorsNew && !isTestingError {
11031069 return true
11041070 }
1071 if !f.imports("errors") {
1072 return true
1073 }
11051074 arg := ce.Args[0]
11061075 ce, ok = arg.(*ast.CallExpr)
11071076 if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") {
11541123 }
11551124 }
11561125
1157 func lintCapAndPunct(s string) (isCap, isPunct bool) {
1126 func lintErrorString(s string) (isClean bool, conf float64) {
1127 const basicConfidence = 0.8
1128 const capConfidence = basicConfidence - 0.2
11581129 first, firstN := utf8.DecodeRuneInString(s)
11591130 last, _ := utf8.DecodeLastRuneInString(s)
1160 isPunct = last == '.' || last == ':' || last == '!'
1161 isCap = unicode.IsUpper(first)
1162 if isCap && len(s) > firstN {
1163 // Don't flag strings starting with something that looks like an initialism.
1164 if second, _ := utf8.DecodeRuneInString(s[firstN:]); unicode.IsUpper(second) {
1165 isCap = false
1166 }
1167 }
1168 return
1169 }
1170
1171 // lintErrorStrings examines error strings. It complains if they are capitalized or end in punctuation.
1131 if last == '.' || last == ':' || last == '!' || last == '\n' {
1132 return false, basicConfidence
1133 }
1134 if unicode.IsUpper(first) {
1135 // People use proper nouns and exported Go identifiers in error strings,
1136 // so decrease the confidence of warnings for capitalization.
1137 if len(s) <= firstN {
1138 return false, capConfidence
1139 }
1140 // Flag strings starting with something that doesn't look like an initialism.
1141 if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) {
1142 return false, capConfidence
1143 }
1144 }
1145 return true, 0
1146 }
1147
1148 // lintErrorStrings examines error strings.
1149 // It complains if they are capitalized or end in punctuation or a newline.
11721150 func (f *file) lintErrorStrings() {
11731151 f.walk(func(node ast.Node) bool {
11741152 ce, ok := node.(*ast.CallExpr)
11891167 if s == "" {
11901168 return true
11911169 }
1192 isCap, isPunct := lintCapAndPunct(s)
1193 var msg string
1194 switch {
1195 case isCap && isPunct:
1196 msg = "error strings should not be capitalized and should not end with punctuation"
1197 case isCap:
1198 msg = "error strings should not be capitalized"
1199 case isPunct:
1200 msg = "error strings should not end with punctuation"
1201 default:
1202 return true
1203 }
1204 // People use proper nouns and exported Go identifiers in error strings,
1205 // so decrease the confidence of warnings for capitalization.
1206 conf := 0.8
1207 if isCap {
1208 conf = 0.6
1209 }
1210 f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"), msg)
1170 clean, conf := lintErrorString(s)
1171 if clean {
1172 return true
1173 }
1174
1175 f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"),
1176 "error strings should not be capitalized or end with punctuation or a newline")
12111177 return true
12121178 })
12131179 }
12281194 name := names[0].Name
12291195 const ref = styleGuideBase + "#receiver-names"
12301196 if name == "_" {
1231 f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore`)
1197 f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore, omit the name if it is unused`)
12321198 return true
12331199 }
12341200 if name == "this" || name == "self" {
12831249 }
12841250 ret := fn.Type.Results.List
12851251 if len(ret) <= 1 {
1252 return true
1253 }
1254 if isIdent(ret[len(ret)-1].Type, "error") {
12861255 return true
12871256 }
12881257 // An error return parameter should be the last parameter.
14311400 }
14321401 key := f.pkg.typesInfo.Types[x.Args[1]]
14331402
1434 if _, ok := key.Type.(*types.Basic); ok {
1403 if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid {
14351404 f.errorf(x, 1.0, category("context"), fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type))
14361405 }
14371406 }
14551424 }
14561425 return true
14571426 })
1427 }
1428
1429 // containsComments returns whether the interval [start, end) contains any
1430 // comments without "// MATCH " prefix.
1431 func (f *file) containsComments(start, end token.Pos) bool {
1432 for _, cgroup := range f.f.Comments {
1433 comments := cgroup.List
1434 if comments[0].Slash >= end {
1435 // All comments starting with this group are after end pos.
1436 return false
1437 }
1438 if comments[len(comments)-1].Slash < start {
1439 // Comments group ends before start pos.
1440 continue
1441 }
1442 for _, c := range comments {
1443 if start <= c.Slash && c.Slash < end && !strings.HasPrefix(c.Text, "// MATCH ") {
1444 return true
1445 }
1446 }
1447 }
1448 return false
14581449 }
14591450
14601451 // receiverType returns the named type of the method receiver, sans "*",
15171508 return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
15181509 }
15191510
1520 func isZero(expr ast.Expr) bool {
1521 lit, ok := expr.(*ast.BasicLit)
1522 return ok && lit.Kind == token.INT && lit.Value == "0"
1523 }
1524
15251511 func isOne(expr ast.Expr) bool {
15261512 lit, ok := expr.(*ast.BasicLit)
15271513 return ok && lit.Kind == token.INT && lit.Value == "1"
1514 }
1515
1516 func isCgoExported(f *ast.FuncDecl) bool {
1517 if f.Recv != nil || f.Doc == nil {
1518 return false
1519 }
1520
1521 cgoExport := regexp.MustCompile(fmt.Sprintf("(?m)^//export %s$", regexp.QuoteMeta(f.Name.Name)))
1522 for _, c := range f.Doc.List {
1523 if cgoExport.MatchString(c.Text) {
1524 return true
1525 }
1526 }
1527 return false
15281528 }
15291529
15301530 var basicTypeKinds = map[types.BasicKind]string{
15851585 return rx.FindStringSubmatch(line)
15861586 }
15871587
1588 // imports returns true if the current file imports the specified package path.
1589 func (f *file) imports(importPath string) bool {
1590 all := astutil.Imports(f.fset, f.f)
1591 for _, p := range all {
1592 for _, i := range p {
1593 uq, err := strconv.Unquote(i.Path.Value)
1594 if err == nil && importPath == uq {
1595 return true
1596 }
1597 }
1598 }
1599 return false
1600 }
1601
15881602 // srcLine returns the complete line at p, including the terminating newline.
15891603 func srcLine(src []byte, p token.Position) string {
15901604 // Run to end of line in both directions if not at line start/end.
287287 }
288288 }
289289 }
290
291 func TestIsGenerated(t *testing.T) {
292 tests := []struct {
293 source string
294 generated bool
295 }{
296 {"// Code Generated by some tool. DO NOT EDIT.", false},
297 {"// Code generated by some tool. DO NOT EDIT.", true},
298 {"// Code generated by some tool. DO NOT EDIT", false},
299 {"// Code generated DO NOT EDIT.", true},
300 {"// Code generated DO NOT EDIT.", false},
301 {"\t\t// Code generated by some tool. DO NOT EDIT.\npackage foo\n", false},
302 {"// Code generated by some tool. DO NOT EDIT.\npackage foo\n", true},
303 {"package foo\n// Code generated by some tool. DO NOT EDIT.\ntype foo int\n", true},
304 {"package foo\n // Code generated by some tool. DO NOT EDIT.\ntype foo int\n", false},
305 {"package foo\n// Code generated by some tool. DO NOT EDIT. \ntype foo int\n", false},
306 {"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.\n", true},
307 {"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.", true},
308 }
309
310 for i, test := range tests {
311 got := isGenerated([]byte(test.source))
312 if got != test.generated {
313 t.Errorf("test %d, isGenerated() = %v, want %v", i, got, test.generated)
314 }
315 }
316 }
00 ;;; golint.el --- lint for the Go source code
11
22 ;; Copyright 2013 The Go Authors. All rights reserved.
3 ;; Use of this source code is governed by a BSD-style
4 ;; license that can be found in the LICENSE file.
53
4 ;; License: BSD-3-clause
65 ;; URL: https://github.com/golang/lint
76
87 ;;; Commentary:
1615 ;; Usage:
1716 ;; C-x `
1817 ;; Jump directly to the line in your code which caused the first message.
19 ;;
18 ;;
2019 ;; For more usage, see Compilation-Mode:
2120 ;; http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html
2221
2322 ;;; Code:
23
2424 (require 'compile)
2525
26 (defun go-lint-buffer-name (mode)
27 "*Golint*")
26 (defun go-lint-buffer-name (mode)
27 "*Golint*")
2828
2929 (defun golint-process-setup ()
3030 "Setup compilation variables and buffer for `golint'."
3535 (set (make-local-variable 'compilation-scroll-output) nil)
3636 (set (make-local-variable 'compilation-disable-input) t)
3737 (set (make-local-variable 'compilation-process-setup-function)
38 'golint-process-setup)
39 )
38 'golint-process-setup))
4039
4140 ;;;###autoload
4241 (defun golint ()
43 "Run golint on the current file and populate the fix list. Pressing C-x ` will jump directly to the line in your code which caused the first message."
42 "Run golint on the current file and populate the fix list.
43 Pressing \"C-x `\" jumps directly to the line in your code which
44 caused the first message."
4445 (interactive)
4546 (compilation-start
4647 (mapconcat #'shell-quote-argument
3333 context.WithValue(c, complex128(1i), "bar") // MATCH /should not use basic type complex128 as key in context.WithValue/
3434 context.WithValue(c, ctxKey{}, "bar") // ok
3535 context.WithValue(c, &ctxKey{}, "bar") // ok
36 context.WithValue(c, invalid{}, "bar") // ok
3637 }
1515 }
1616 return false
1717 }
18
19 func g(x int) int {
20 if x == 0 {
21 log.Print("x is zero")
22 } else if x > 9 {
23 return 2
24 } else if x > 0 {
25 return 1
26 } else {
27 log.Printf("non-positive x: %d", x)
28 }
29 return 0
30 }
31
32 func h(x int) int {
33 if x == 0 {
34 log.Print("x is zero")
35 } else if x > 99 {
36 return 3
37 } else if x > 9 {
38 return 2
39 } else if x > 0 {
40 return 1
41 } else {
42 log.Printf("non-positive x: %d", x)
43 }
44 return 0
45 }
4040 func m() (x int, err error, y int) { // MATCH /error should be the last type/
4141 return 0, nil, 0
4242 }
43
44 // Check for multiple error returns but with errors at the end.
45 func n() (int, error, error) { // OK
46 return 0, nil, nil
47 }
48
49 // Check for multiple error returns mixed in order, but keeping one error at last position.
50 func o() (int, error, int, error) { // OK
51 return 0, nil, 0, nil
52 }
0 // Test for allowed errors.New(fmt.Sprintf()) when a custom errors package is imported.
1
2 // Package foo ...
3 package foo
4
5 import (
6 "fmt"
7
8 "github.com/pkg/errors"
9 )
10
11 func f(x int) error {
12 if x > 10 {
13 return errors.New(fmt.Sprintf("something %d", x)) // OK
14 }
15 if x > 5 {
16 return errors.New(g("blah")) // OK
17 }
18 if x > 4 {
19 return errors.New("something else") // OK
20 }
21 return nil
22 }
23
24 func g(s string) string { return "prefix: " + s }
2626 // Check for the error strings themselves.
2727
2828 func g(x int) error {
29 if x < 1 {
30 return fmt.Errorf("This %d is too low", x) // MATCH /error strings.*not be capitalized/
31 } else if x == 0 {
32 return fmt.Errorf("XML time") // ok
33 }
34 return errors.New(`too much stuff.`) // MATCH /error strings.*not end with punctuation/
29 var err error
30 err = fmt.Errorf("This %d is too low", x) // MATCH /error strings.*be capitalized/
31 err = fmt.Errorf("XML time") // ok
32 err = fmt.Errorf("newlines are fun\n") // MATCH /error strings.*end with punctuation/
33 err = fmt.Errorf("Newlines are really fun\n") // MATCH /error strings.+not be capitalized/
34 err = errors.New(`too much stuff.`) // MATCH /error strings.*end with punctuation/
35 err = errors.New("This %d is too low", x) // MATCH /error strings.*be capitalized/
36 return err
3537 }
88 net_http "net/http" // renamed deliberately
99 "net/url"
1010 )
11
12 import "C"
1113
1214 var var_name int // MATCH /underscore.*var.*var_name/
1315
5658 CPP_CONST = 1 // MATCH /ALL_CAPS.*CamelCase/
5759 kLeadingKay = 2 // MATCH /k.*leadingKay/
5860
59 HTML = 3 // okay; no underscore
60 X509B = 4 // ditto
61 HTML = 3 // okay; no underscore
62 X509B = 4 // ditto
63 V1_10_5 = 5 // okay; fewer than two uppercase letters
6164 )
65
66 var kVarsAreSometimesUsedAsConstants = 0 // MATCH /k.*varsAreSometimesUsedAsConstants/
67 var (
68 kVarsAreSometimesUsedAsConstants2 = 0 // MATCH /k.*varsAreSometimesUsedAsConstants2/
69 )
70
71 var kThisIsNotOkay = struct { // MATCH /k.*thisIsNotOkay/
72 kThisIsOkay bool
73 }{}
74
75 func kThisIsOkay() { // this is okay because this is a function name
76 var kThisIsAlsoOkay = 1 // this is okay because this is a non-top-level variable
77 _ = kThisIsAlsoOkay
78 const kThisIsNotOkay = 2 // MATCH /k.*thisIsNotOkay/
79 }
80
81 var anotherFunctionScope = func() {
82 var kThisIsOkay = 1 // this is okay because this is a non-top-level variable
83 _ = kThisIsOkay
84 const kThisIsNotOkay = 2 // MATCH /k.*thisIsNotOkay/}
85 }
6286
6387 func f(bad_name int) {} // MATCH /underscore.*func parameter.*bad_name/
6488 func g() (no_way int) { return 0 } // MATCH /underscore.*func result.*no_way/
88112 type t struct{}
89113
90114 func (t) LastInsertId() (int64, error) { return 0, nil } // okay because it matches a known style violation
115
116 //export exported_to_c
117 func exported_to_c() {} // okay: https://github.com/golang/lint/issues/144
118
119 //export exported_to_c_with_arg
120 func exported_to_c_with_arg(but_use_go_param_names int) // MATCH /underscore.*func parameter.*but_use_go_param_names/
121
122 // This is an exported C function with a leading doc comment.
123 //
124 //export exported_to_c_with_comment
125 func exported_to_c_with_comment() {} // okay: https://github.com/golang/lint/issues/144
126
127 //export maybe_exported_to_CPlusPlusWithCamelCase
128 func maybe_exported_to_CPlusPlusWithCamelCase() {} // okay: https://github.com/golang/lint/issues/144
129
130 // WhyAreYouUsingCapitalLetters_InACFunctionName is a Go-exported function that
131 // is also exported to C as a name with underscores.
132 //
133 // Don't do that. If you want to use a C-style name for a C export, make it
134 // lower-case and leave it out of the Go-exported API.
135 //
136 //export WhyAreYouUsingCapitalLetters_InACFunctionName
137 func WhyAreYouUsingCapitalLetters_InACFunctionName() {} // MATCH /underscore.*func.*Why.*CFunctionName/
0 // MixedCaps package name
1
2 // Package PkgName ...
3 package PkgName // MATCH /don't use MixedCaps in package name/
1313 var y string
1414 _ = y
1515 for y, _ = range m { // MATCH /should omit 2nd value.*range.*equivalent.*for y = range/
16 }
17
18 for _ = range m { // MATCH /should omit values.*range.*equivalent.*for range/
19 }
20
21 for _, _ = range m { // MATCH /should omit values.*range.*equivalent.*for range/
1622 }
1723
1824 // all OK:
+0
-86
testdata/var-decl.go less more
0 // Test for redundant type declaration.
1
2 // Package foo ...
3 package foo
4
5 import (
6 "fmt"
7 "io"
8 "net/http"
9 "nosuchpkg" // export data unavailable
10 "os"
11 )
12
13 // Q is a test type.
14 type Q bool
15
16 var myInt int = 7 // MATCH /should.*int.*myInt.*inferred/
17 var mux *http.ServeMux = http.NewServeMux() // MATCH /should.*\*http\.ServeMux.*inferred/
18
19 var myZeroInt int = 0 // MATCH /should.*= 0.*myZeroInt.*zero value/
20 var myZeroFlt float32 = 0. // MATCH /should.*= 0\..*myZeroFlt.*zero value/
21 var myZeroF64 float64 = 0.0 // MATCH /should.*= 0\..*myZeroF64.*zero value/
22 var myZeroImg complex64 = 0i // MATCH /should.*= 0i.*myZeroImg.*zero value/
23 var myZeroStr string = "" // MATCH /should.*= "".*myZeroStr.*zero value/
24 var myZeroRaw string = `` // MATCH /should.*= ``.*myZeroRaw.*zero value/
25 var myZeroPtr *Q = nil // MATCH /should.*= nil.*myZeroPtr.*zero value/
26 var myZeroRune rune = '\x00' // MATCH /should.*= '\\x00'.*myZeroRune.*zero value/
27 var myZeroRune2 rune = '\000' // MATCH /should.*= '\\000'.*myZeroRune2.*zero value/
28
29 // No warning because there's no type on the LHS
30 var x = 0
31
32 // This shouldn't get a warning because there's no initial values.
33 var str fmt.Stringer
34
35 // No warning because this is a const.
36 const k uint64 = 7
37
38 const num = 123
39
40 // No warning because the var's RHS is known to be an untyped const.
41 var flags uint32 = num
42
43 // No warnings because the RHS is an ideal int, and the LHS is a different int type.
44 var userID int64 = 1235
45 var negID int64 = -1
46 var parenID int64 = (17)
47 var crazyID int64 = -(-(-(-9)))
48
49 // Same, but for strings and floats.
50 type stringT string
51 type floatT float64
52
53 var stringV stringT = "abc"
54 var floatV floatT = 123.45
55
56 // No warning because the LHS names an interface type.
57 var data interface{} = googleIPs
58 var googleIPs []int
59
60 // No warning because it's a common idiom for interface satisfaction.
61 var _ Server = (*serverImpl)(nil)
62
63 // Server is a test type.
64 type Server interface{}
65 type serverImpl struct{}
66
67 // LHS is a different type than the RHS.
68 var myStringer fmt.Stringer = q(0)
69
70 // LHS is a different type than the RHS.
71 var out io.Writer = os.Stdout
72
73 var out2 io.Writer = newWriter() // MATCH /should omit.*io\.Writer/
74
75 func newWriter() io.Writer { return nil }
76
77 // We don't figure out the true types of LHS and RHS here,
78 // so we suppress the check.
79 var ni nosuchpkg.Interface = nosuchpkg.NewConcrete()
80
81 var y string = q(1).String() // MATCH /should.*string/
82
83 type q int
84
85 func (q) String() string { return "I'm a q" }