pikeman 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +21 -0
  4. data/CONTRIBUTING.md +15 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +36 -0
  7. data/LICENSE +27 -0
  8. data/LICENSE.txt +21 -0
  9. data/Makefile +39 -0
  10. data/README.md +85 -0
  11. data/Rakefile +10 -0
  12. data/VERSION +1 -0
  13. data/bin/console +14 -0
  14. data/bin/pikeman +15 -0
  15. data/bin/setup +8 -0
  16. data/golint/golint.go +213 -0
  17. data/golint/import.go +309 -0
  18. data/golint/version.go +3 -0
  19. data/lib/pikeman.rb +34 -0
  20. data/lib/pikeman/version.rb +3 -0
  21. data/lint.go +1708 -0
  22. data/lint_test.go +317 -0
  23. data/misc/emacs/golint.el +54 -0
  24. data/misc/vim/ftplugin/go/lint.vim +31 -0
  25. data/pikeman.gemspec +28 -0
  26. data/testdata/4.go +38 -0
  27. data/testdata/5_test.go +17 -0
  28. data/testdata/blank-import-lib.go +39 -0
  29. data/testdata/blank-import-lib_test.go +25 -0
  30. data/testdata/blank-import-main.go +14 -0
  31. data/testdata/broken.go +9 -0
  32. data/testdata/common-methods.go +16 -0
  33. data/testdata/const-block.go +36 -0
  34. data/testdata/context.go +24 -0
  35. data/testdata/contextkeytypes.go +38 -0
  36. data/testdata/else-multi.go +18 -0
  37. data/testdata/else.go +23 -0
  38. data/testdata/error-return.go +43 -0
  39. data/testdata/errorf.go +40 -0
  40. data/testdata/errors.go +38 -0
  41. data/testdata/iferr.go +101 -0
  42. data/testdata/import-dot.go +8 -0
  43. data/testdata/inc.go +14 -0
  44. data/testdata/names.go +116 -0
  45. data/testdata/pkg-caps.go +4 -0
  46. data/testdata/pkg-doc1.go +3 -0
  47. data/testdata/pkg-doc2.go +5 -0
  48. data/testdata/pkg-doc3.go +7 -0
  49. data/testdata/pkg-doc4.go +7 -0
  50. data/testdata/pkg-doc5.go +9 -0
  51. data/testdata/pkg-main.go +5 -0
  52. data/testdata/range.go +43 -0
  53. data/testdata/receiver-names.go +49 -0
  54. data/testdata/sort.go +20 -0
  55. data/testdata/stutter.go +25 -0
  56. data/testdata/time.go +13 -0
  57. data/testdata/unexp-return.go +46 -0
  58. data/testdata/var-decl.go +86 -0
  59. metadata +172 -0
@@ -0,0 +1,317 @@
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
8
+
9
+ import (
10
+ "bytes"
11
+ "flag"
12
+ "fmt"
13
+ "go/ast"
14
+ "go/parser"
15
+ "go/printer"
16
+ "go/token"
17
+ "go/types"
18
+ "io/ioutil"
19
+ "path"
20
+ "regexp"
21
+ "strconv"
22
+ "strings"
23
+ "testing"
24
+ )
25
+
26
+ var lintMatch = flag.String("lint.match", "", "restrict testdata matches to this pattern")
27
+
28
+ func TestAll(t *testing.T) {
29
+ l := new(Linter)
30
+ rx, err := regexp.Compile(*lintMatch)
31
+ if err != nil {
32
+ t.Fatalf("Bad -lint.match value %q: %v", *lintMatch, err)
33
+ }
34
+
35
+ baseDir := "testdata"
36
+ fis, err := ioutil.ReadDir(baseDir)
37
+ if err != nil {
38
+ t.Fatalf("ioutil.ReadDir: %v", err)
39
+ }
40
+ if len(fis) == 0 {
41
+ t.Fatalf("no files in %v", baseDir)
42
+ }
43
+ for _, fi := range fis {
44
+ if !rx.MatchString(fi.Name()) {
45
+ continue
46
+ }
47
+ //t.Logf("Testing %s", fi.Name())
48
+ src, err := ioutil.ReadFile(path.Join(baseDir, fi.Name()))
49
+ if err != nil {
50
+ t.Fatalf("Failed reading %s: %v", fi.Name(), err)
51
+ }
52
+
53
+ ins := parseInstructions(t, fi.Name(), src)
54
+ if ins == nil {
55
+ t.Errorf("Test file %v does not have instructions", fi.Name())
56
+ continue
57
+ }
58
+
59
+ ps, err := l.Lint(fi.Name(), src)
60
+ if err != nil {
61
+ t.Errorf("Linting %s: %v", fi.Name(), err)
62
+ continue
63
+ }
64
+
65
+ for _, in := range ins {
66
+ ok := false
67
+ for i, p := range ps {
68
+ if p.Position.Line != in.Line {
69
+ continue
70
+ }
71
+ if in.Match.MatchString(p.Text) {
72
+ // check replacement if we are expecting one
73
+ if in.Replacement != "" {
74
+ // ignore any inline comments, since that would be recursive
75
+ r := p.ReplacementLine
76
+ if i := strings.Index(r, " //"); i >= 0 {
77
+ r = r[:i]
78
+ }
79
+ if r != in.Replacement {
80
+ t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", fi.Name(), in.Line, r, in.Replacement)
81
+ }
82
+ }
83
+
84
+ // remove this problem from ps
85
+ copy(ps[i:], ps[i+1:])
86
+ ps = ps[:len(ps)-1]
87
+
88
+ //t.Logf("/%v/ matched at %s:%d", in.Match, fi.Name(), in.Line)
89
+ ok = true
90
+ break
91
+ }
92
+ }
93
+ if !ok {
94
+ t.Errorf("Lint failed at %s:%d; /%v/ did not match", fi.Name(), in.Line, in.Match)
95
+ }
96
+ }
97
+ for _, p := range ps {
98
+ t.Errorf("Unexpected problem at %s:%d: %v", fi.Name(), p.Position.Line, p.Text)
99
+ }
100
+ }
101
+ }
102
+
103
+ type instruction struct {
104
+ Line int // the line number this applies to
105
+ Match *regexp.Regexp // what pattern to match
106
+ Replacement string // what the suggested replacement line should be
107
+ }
108
+
109
+ // parseInstructions parses instructions from the comments in a Go source file.
110
+ // It returns nil if none were parsed.
111
+ func parseInstructions(t *testing.T, filename string, src []byte) []instruction {
112
+ fset := token.NewFileSet()
113
+ f, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
114
+ if err != nil {
115
+ t.Fatalf("Test file %v does not parse: %v", filename, err)
116
+ }
117
+ var ins []instruction
118
+ for _, cg := range f.Comments {
119
+ ln := fset.Position(cg.Pos()).Line
120
+ raw := cg.Text()
121
+ for _, line := range strings.Split(raw, "\n") {
122
+ if line == "" || strings.HasPrefix(line, "#") {
123
+ continue
124
+ }
125
+ if line == "OK" && ins == nil {
126
+ // so our return value will be non-nil
127
+ ins = make([]instruction, 0)
128
+ continue
129
+ }
130
+ if strings.Contains(line, "MATCH") {
131
+ rx, err := extractPattern(line)
132
+ if err != nil {
133
+ t.Fatalf("At %v:%d: %v", filename, ln, err)
134
+ }
135
+ matchLine := ln
136
+ if i := strings.Index(line, "MATCH:"); i >= 0 {
137
+ // This is a match for a different line.
138
+ lns := strings.TrimPrefix(line[i:], "MATCH:")
139
+ lns = lns[:strings.Index(lns, " ")]
140
+ matchLine, err = strconv.Atoi(lns)
141
+ if err != nil {
142
+ t.Fatalf("Bad match line number %q at %v:%d: %v", lns, filename, ln, err)
143
+ }
144
+ }
145
+ var repl string
146
+ if r, ok := extractReplacement(line); ok {
147
+ repl = r
148
+ }
149
+ ins = append(ins, instruction{
150
+ Line: matchLine,
151
+ Match: rx,
152
+ Replacement: repl,
153
+ })
154
+ }
155
+ }
156
+ }
157
+ return ins
158
+ }
159
+
160
+ func extractPattern(line string) (*regexp.Regexp, error) {
161
+ a, b := strings.Index(line, "/"), strings.LastIndex(line, "/")
162
+ if a == -1 || a == b {
163
+ return nil, fmt.Errorf("malformed match instruction %q", line)
164
+ }
165
+ pat := line[a+1 : b]
166
+ rx, err := regexp.Compile(pat)
167
+ if err != nil {
168
+ return nil, fmt.Errorf("bad match pattern %q: %v", pat, err)
169
+ }
170
+ return rx, nil
171
+ }
172
+
173
+ func extractReplacement(line string) (string, bool) {
174
+ // Look for this: / -> `
175
+ // (the end of a match and start of a backtick string),
176
+ // and then the closing backtick.
177
+ const start = "/ -> `"
178
+ a, b := strings.Index(line, start), strings.LastIndex(line, "`")
179
+ if a < 0 || a > b {
180
+ return "", false
181
+ }
182
+ return line[a+len(start) : b], true
183
+ }
184
+
185
+ func render(fset *token.FileSet, x interface{}) string {
186
+ var buf bytes.Buffer
187
+ if err := printer.Fprint(&buf, fset, x); err != nil {
188
+ panic(err)
189
+ }
190
+ return buf.String()
191
+ }
192
+
193
+ func TestLine(t *testing.T) {
194
+ tests := []struct {
195
+ src string
196
+ offset int
197
+ want string
198
+ }{
199
+ {"single line file", 5, "single line file"},
200
+ {"single line file with newline\n", 5, "single line file with newline\n"},
201
+ {"first\nsecond\nthird\n", 2, "first\n"},
202
+ {"first\nsecond\nthird\n", 9, "second\n"},
203
+ {"first\nsecond\nthird\n", 14, "third\n"},
204
+ {"first\nsecond\nthird with no newline", 16, "third with no newline"},
205
+ {"first byte\n", 0, "first byte\n"},
206
+ }
207
+ for _, test := range tests {
208
+ got := srcLine([]byte(test.src), token.Position{Offset: test.offset})
209
+ if got != test.want {
210
+ t.Errorf("srcLine(%q, offset=%d) = %q, want %q", test.src, test.offset, got, test.want)
211
+ }
212
+ }
213
+ }
214
+
215
+ func TestLintName(t *testing.T) {
216
+ tests := []struct {
217
+ name, want string
218
+ }{
219
+ {"foo_bar", "fooBar"},
220
+ {"foo_bar_baz", "fooBarBaz"},
221
+ {"Foo_bar", "FooBar"},
222
+ {"foo_WiFi", "fooWiFi"},
223
+ {"id", "id"},
224
+ {"Id", "ID"},
225
+ {"foo_id", "fooID"},
226
+ {"fooId", "fooID"},
227
+ {"fooUid", "fooUID"},
228
+ {"idFoo", "idFoo"},
229
+ {"uidFoo", "uidFoo"},
230
+ {"midIdDle", "midIDDle"},
231
+ {"APIProxy", "APIProxy"},
232
+ {"ApiProxy", "APIProxy"},
233
+ {"apiProxy", "apiProxy"},
234
+ {"_Leading", "_Leading"},
235
+ {"___Leading", "_Leading"},
236
+ {"trailing_", "trailing"},
237
+ {"trailing___", "trailing"},
238
+ {"a_b", "aB"},
239
+ {"a__b", "aB"},
240
+ {"a___b", "aB"},
241
+ {"Rpc1150", "RPC1150"},
242
+ {"case3_1", "case3_1"},
243
+ {"case3__1", "case3_1"},
244
+ {"IEEE802_16bit", "IEEE802_16bit"},
245
+ {"IEEE802_16Bit", "IEEE802_16Bit"},
246
+ }
247
+ for _, test := range tests {
248
+ got := lintName(test.name)
249
+ if got != test.want {
250
+ t.Errorf("lintName(%q) = %q, want %q", test.name, got, test.want)
251
+ }
252
+ }
253
+ }
254
+
255
+ func TestExportedType(t *testing.T) {
256
+ tests := []struct {
257
+ typString string
258
+ exp bool
259
+ }{
260
+ {"int", true},
261
+ {"string", false}, // references the shadowed builtin "string"
262
+ {"T", true},
263
+ {"t", false},
264
+ {"*T", true},
265
+ {"*t", false},
266
+ {"map[int]complex128", true},
267
+ }
268
+ for _, test := range tests {
269
+ src := `package foo; type T int; type t int; type string struct{}`
270
+ fset := token.NewFileSet()
271
+ file, err := parser.ParseFile(fset, "foo.go", src, 0)
272
+ if err != nil {
273
+ t.Fatalf("Parsing %q: %v", src, err)
274
+ }
275
+ // use the package name as package path
276
+ config := &types.Config{}
277
+ pkg, err := config.Check(file.Name.Name, fset, []*ast.File{file}, nil)
278
+ if err != nil {
279
+ t.Fatalf("Type checking %q: %v", src, err)
280
+ }
281
+ tv, err := types.Eval(fset, pkg, token.NoPos, test.typString)
282
+ if err != nil {
283
+ t.Errorf("types.Eval(%q): %v", test.typString, err)
284
+ continue
285
+ }
286
+ if got := exportedType(tv.Type); got != test.exp {
287
+ t.Errorf("exportedType(%v) = %t, want %t", tv.Type, got, test.exp)
288
+ }
289
+ }
290
+ }
291
+
292
+ func TestIsGenerated(t *testing.T) {
293
+ tests := []struct {
294
+ source string
295
+ generated bool
296
+ }{
297
+ {"// Code Generated by some tool. DO NOT EDIT.", false},
298
+ {"// Code generated by some tool. DO NOT EDIT.", true},
299
+ {"// Code generated by some tool. DO NOT EDIT", false},
300
+ {"// Code generated DO NOT EDIT.", true},
301
+ {"// Code generated DO NOT EDIT.", false},
302
+ {"\t\t// Code generated by some tool. DO NOT EDIT.\npackage foo\n", false},
303
+ {"// Code generated by some tool. DO NOT EDIT.\npackage foo\n", true},
304
+ {"package foo\n// Code generated by some tool. DO NOT EDIT.\ntype foo int\n", true},
305
+ {"package foo\n // Code generated by some tool. DO NOT EDIT.\ntype foo int\n", false},
306
+ {"package foo\n// Code generated by some tool. DO NOT EDIT. \ntype foo int\n", false},
307
+ {"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.\n", true},
308
+ {"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.", true},
309
+ }
310
+
311
+ for i, test := range tests {
312
+ got := isGenerated([]byte(test.source))
313
+ if got != test.generated {
314
+ t.Errorf("test %d, isGenerated() = %v, want %v", i, got, test.generated)
315
+ }
316
+ }
317
+ }
@@ -0,0 +1,54 @@
1
+ ;;; golint.el --- lint for the Go source code
2
+
3
+ ;; Copyright 2013 The Go Authors. All rights reserved.
4
+
5
+ ;; License: BSD-3-clause
6
+ ;; URL: https://github.com/golang/lint
7
+
8
+ ;;; Commentary:
9
+
10
+ ;; To install golint, add the following lines to your .emacs file:
11
+ ;; (add-to-list 'load-path "PATH CONTAINING golint.el" t)
12
+ ;; (require 'golint)
13
+ ;;
14
+ ;; After this, type M-x golint on Go source code.
15
+ ;;
16
+ ;; Usage:
17
+ ;; C-x `
18
+ ;; Jump directly to the line in your code which caused the first message.
19
+ ;;
20
+ ;; For more usage, see Compilation-Mode:
21
+ ;; http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html
22
+
23
+ ;;; Code:
24
+
25
+ (require 'compile)
26
+
27
+ (defun go-lint-buffer-name (mode)
28
+ "*Golint*")
29
+
30
+ (defun golint-process-setup ()
31
+ "Setup compilation variables and buffer for `golint'."
32
+ (run-hooks 'golint-setup-hook))
33
+
34
+ (define-compilation-mode golint-mode "golint"
35
+ "Golint is a linter for Go source code."
36
+ (set (make-local-variable 'compilation-scroll-output) nil)
37
+ (set (make-local-variable 'compilation-disable-input) t)
38
+ (set (make-local-variable 'compilation-process-setup-function)
39
+ 'golint-process-setup))
40
+
41
+ ;;;###autoload
42
+ (defun golint ()
43
+ "Run golint on the current file and populate the fix list.
44
+ Pressing \"C-x `\" jumps directly to the line in your code which
45
+ caused the first message."
46
+ (interactive)
47
+ (compilation-start
48
+ (mapconcat #'shell-quote-argument
49
+ (list "golint" (expand-file-name buffer-file-name)) " ")
50
+ 'golint-mode))
51
+
52
+ (provide 'golint)
53
+
54
+ ;;; golint.el ends here
@@ -0,0 +1,31 @@
1
+ " Copyright 2013 The Go Authors. All rights reserved.
2
+ " Use of this source code is governed by a BSD-style
3
+ " license that can be found in the LICENSE file.
4
+ "
5
+ " lint.vim: Vim command to lint Go files with golint.
6
+ "
7
+ " https://github.com/golang/lint
8
+ "
9
+ " This filetype plugin add a new commands for go buffers:
10
+ "
11
+ " :Lint
12
+ "
13
+ " Run golint for the current Go file.
14
+ "
15
+ if exists("b:did_ftplugin_go_lint")
16
+ finish
17
+ endif
18
+
19
+ if !executable("golint")
20
+ finish
21
+ endif
22
+
23
+ command! -buffer Lint call s:GoLint()
24
+
25
+ function! s:GoLint() abort
26
+ cexpr system('golint ' . shellescape(expand('%')))
27
+ endfunction
28
+
29
+ let b:did_ftplugin_go_lint = 1
30
+
31
+ " vim:ts=4:sw=4:et
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "pikeman/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "pikeman"
7
+ spec.version = Pikeman::VERSION
8
+ spec.authors = ["Maxime Bedard"]
9
+ spec.email = ["maxime.bedard@shopify.com"]
10
+
11
+ spec.summary = %q{Very opinionated version of google/golint}
12
+ spec.description = %q{Very opinionated version of google/golint}
13
+ spec.homepage = "https://github.com/maximebedard/pikeman"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+
20
+ spec.executables = ["pikeman"]
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "cli-kit"
24
+ spec.add_development_dependency "bundler", "~> 1.16"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ spec.add_development_dependency "pry-byebug"
28
+ end
@@ -0,0 +1,38 @@
1
+ // Test that exported names have correct comments.
2
+
3
+ // Package pkg does something.
4
+ package pkg
5
+
6
+ import "time"
7
+
8
+ type T int // MATCH /exported type T.*should.*comment.*or.*unexport/
9
+
10
+ func (T) F() {} // MATCH /exported method T\.F.*should.*comment.*or.*unexport/
11
+
12
+ // this is a nice type.
13
+ // MATCH /comment.*exported type U.*should.*form.*"U ..."/
14
+ type U string
15
+
16
+ // this is a neat function.
17
+ // MATCH /comment.*exported method U\.G.*should.*form.*"G ..."/
18
+ func (U) G() {}
19
+
20
+ // A V is a string.
21
+ type V string
22
+
23
+ // V.H has a pointer receiver
24
+
25
+ func (*V) H() {} // MATCH /exported method V\.H.*should.*comment.*or.*unexport/
26
+
27
+ var W = "foo" // MATCH /exported var W.*should.*comment.*or.*unexport/
28
+
29
+ const X = "bar" // MATCH /exported const X.*should.*comment.*or.*unexport/
30
+
31
+ var Y, Z int // MATCH /exported var Z.*own declaration/
32
+
33
+ // Location should be okay, since the other var name is an underscore.
34
+ var Location, _ = time.LoadLocation("Europe/Istanbul") // not Constantinople
35
+
36
+ // this is improperly documented
37
+ // MATCH /comment.*const.*Thing.*form.*"Thing ..."/
38
+ const Thing = "wonderful"