pikeman 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9bbf6e9cacc31c9a512e66104fe7943f0ba6c25
4
- data.tar.gz: 0e7a8b7e2c3dea679a0a785332606fe5879913eb
3
+ metadata.gz: 057a1f0cc477bb4316783ce4dc996a87715c16a3
4
+ data.tar.gz: 17e49b302e8097fb73c68dfc56ad5f063fa281d3
5
5
  SHA512:
6
- metadata.gz: dde570ed22b983773f42759688942d97d32674e05c741b53b4b4a2377a7fe2f2da210b37236df577b22c2b3b299500f515fbf9ed5ec83c4751bf53b0e96f655b
7
- data.tar.gz: 0dbd6801118e61935afa61ec49b67f933e1a6b41f5989149b76c01d7a68fcf58d7419488919ae9975848a45998efd7746c908b71a67a94ccc2857d523230d317
6
+ metadata.gz: 202415699de652a837e0cba7948ba864a381a37ad9c8d782bf52b31c649ecafeb23e30db3d7c9d688c176c95465f127390eacaa338b771b1796a83fc18327a13
7
+ data.tar.gz: c0274d74155c6ddb3bdc66a388fdc477511cac887d46256eb5515bb288bd88910bc16e5fca9a4a08255cce57a45fe2002f9c45dc17bdfddb646ba3d7301b82ff
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pikeman (0.0.1)
4
+ pikeman (0.0.3)
5
5
  cli-kit (~> 3.0)
6
6
 
7
7
  GEM
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
@@ -0,0 +1,218 @@
1
+ package lint
2
+
3
+ import (
4
+ "bytes"
5
+ "io"
6
+ "os"
7
+ "path/filepath"
8
+
9
+ "gopkg.in/yaml.v2"
10
+ )
11
+
12
+ var defaultConfig = []byte(`
13
+ Includes:
14
+ - ./**/*.go
15
+
16
+ Excludes:
17
+ - ./vendor/**
18
+ - ./pkg/**
19
+
20
+ PackageComment:
21
+ Enabled: true
22
+
23
+ Imports:
24
+ Enabled: true
25
+
26
+ BlankImports:
27
+ Enabled: true
28
+
29
+ Exported:
30
+ Enabled: true
31
+
32
+ Names:
33
+ Enabled: true
34
+
35
+ VarDecls:
36
+ Enabled: true
37
+
38
+ Elses:
39
+ Enabled: true
40
+
41
+ IfError:
42
+ Enabled: true
43
+
44
+ Ranges:
45
+ Enabled: true
46
+
47
+ Errorf:
48
+ Enabled: true
49
+
50
+ Errors:
51
+ Enabled: true
52
+
53
+ ErrorStrings:
54
+ Enabled: true
55
+
56
+ ReceiverNames:
57
+ Enabled: true
58
+
59
+ IncDec:
60
+ Enabled: true
61
+
62
+ ErrorReturn:
63
+ Enabled: true
64
+
65
+ UnexportedReturn:
66
+ Enabled: true
67
+
68
+ TimeNames:
69
+ Enabled: true
70
+
71
+ ContextKeyTypes:
72
+ Enabled: true
73
+
74
+ ContextArgs:
75
+ Enabled: true
76
+ `)
77
+
78
+ type Rule interface {
79
+ IsEnabled() bool
80
+ }
81
+
82
+ type rule struct {
83
+ Enabled bool `yaml:"Enabled"`
84
+ }
85
+
86
+ func (r rule) IsEnabled() bool { return r.Enabled }
87
+
88
+ type PackageCommentRule struct {
89
+ rule `yaml:",inline"`
90
+ }
91
+ type ImportsRule struct {
92
+ rule `yaml:",inline"`
93
+ }
94
+ type BlankImportsRule struct {
95
+ rule `yaml:",inline"`
96
+ }
97
+ type ExportedRule struct {
98
+ rule `yaml:",inline"`
99
+ }
100
+ type NamesRule struct {
101
+ rule `yaml:",inline"`
102
+ }
103
+ type VarDeclsRule struct {
104
+ rule `yaml:",inline"`
105
+ }
106
+ type ElsesRule struct {
107
+ rule `yaml:",inline"`
108
+ }
109
+ type IfErrorRule struct {
110
+ rule `yaml:",inline"`
111
+ }
112
+ type RangesRule struct {
113
+ rule `yaml:",inline"`
114
+ }
115
+ type ErrorfRule struct {
116
+ rule `yaml:",inline"`
117
+ }
118
+ type ErrorsRule struct {
119
+ rule `yaml:",inline"`
120
+ }
121
+ type ErrorStringsRule struct {
122
+ rule `yaml:",inline"`
123
+ }
124
+ type ReceiverNamesRule struct {
125
+ rule `yaml:",inline"`
126
+ }
127
+ type IncDecRule struct {
128
+ rule `yaml:",inline"`
129
+ }
130
+ type ErrorReturnRule struct {
131
+ rule `yaml:",inline"`
132
+ }
133
+ type UnexportedReturnRule struct {
134
+ rule `yaml:",inline"`
135
+ }
136
+ type TimeNamesRule struct {
137
+ rule `yaml:",inline"`
138
+ }
139
+ type ContextKeyTypesRule struct {
140
+ rule `yaml:",inline"`
141
+ }
142
+ type ContextArgsRule struct {
143
+ rule `yaml:",inline"`
144
+ }
145
+
146
+ type Config struct {
147
+ Includes []string `yaml:"Includes"`
148
+ Excludes []string `yaml:"Excludes"`
149
+
150
+ PackageComment PackageCommentRule `yaml:"PackageComment"`
151
+ Imports ImportsRule `yaml:"Imports"`
152
+ BlankImports BlankImportsRule `yaml:"BlankImports"`
153
+ Exported ExportedRule `yaml:"Exported"`
154
+ Names NamesRule `yaml:"Names"`
155
+ VarDecls VarDeclsRule `yaml:"VarDecls"`
156
+ Elses ElsesRule `yaml:"Elses"`
157
+ IfError IfErrorRule `yaml:"IfError"`
158
+ Ranges RangesRule `yaml:"Ranges"`
159
+ Errorf ErrorfRule `yaml:"Errorf"`
160
+ Errors ErrorsRule `yaml:"Errors"`
161
+ ErrorStrings ErrorStringsRule `yaml:"ErrorStrings"`
162
+ ReceiverNames ReceiverNamesRule `yaml:"ReceiverNames"`
163
+ IncDec IncDecRule `yaml:"IncDec"`
164
+ ErrorReturn ErrorReturnRule `yaml:"ErrorReturn"`
165
+ UnexportedReturn UnexportedReturnRule `yaml:"UnexportedReturn"`
166
+ TimeNames TimeNamesRule `yaml:"TimeNames"`
167
+ ContextKeyTypes ContextKeyTypesRule `yaml:"ContextKeyTypes"`
168
+ ContextArgs ContextArgsRule `yaml:"ContextArgs"`
169
+ }
170
+
171
+ func ReadConfigFromWorkingDir() (*Config, error) {
172
+ wd, err := os.Getwd()
173
+ if err != nil {
174
+ return nil, err
175
+ }
176
+
177
+ reader, err := backtrackConfig(wd)
178
+ if err != nil {
179
+ return nil, err
180
+ }
181
+
182
+ return decodeConfig(reader)
183
+ }
184
+
185
+ func ReadConfig(path string) (*Config, error) {
186
+ reader, err := os.Open(path)
187
+ if err != nil {
188
+ return nil, err
189
+ }
190
+
191
+ return decodeConfig(reader)
192
+ }
193
+
194
+ func decodeConfig(reader io.Reader) (*Config, error) {
195
+ var config Config
196
+ err := yaml.NewDecoder(reader).Decode(&config)
197
+ if err != nil {
198
+ return nil, err
199
+ }
200
+ return &config, nil
201
+ }
202
+
203
+ func backtrackConfig(wd string) (io.Reader, error) {
204
+ configPath := filepath.Join(wd, ".pikeman.yml")
205
+ _, err := os.Stat(configPath)
206
+
207
+ switch {
208
+ case os.IsNotExist(err) && wd == "/":
209
+ return bytes.NewReader(defaultConfig), nil
210
+ case os.IsNotExist(err):
211
+ previousDir := filepath.Dir(filepath.Join(wd, ".."))
212
+ return backtrackConfig(previousDir)
213
+ case err == nil:
214
+ return os.Open(configPath)
215
+ default:
216
+ return nil, err
217
+ }
218
+ }
@@ -17,15 +17,17 @@ import (
17
17
  "path/filepath"
18
18
  "strings"
19
19
 
20
- "golang.org/x/lint"
20
+ "github.com/maximebedard/pikeman"
21
21
  )
22
22
 
23
23
  var (
24
24
  minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
25
25
  setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
26
26
  formatterType = flag.String("format", "text", "set the format. Available: text, json.")
27
+ configPath = flag.String("config_path", "", "set the configuration file.")
27
28
  suggestions int
28
29
  formatter problemFormatter
30
+ config *lint.Config
29
31
  )
30
32
 
31
33
  func usage() {
@@ -50,6 +52,18 @@ func main() {
50
52
  formatter = &textFormatter{}
51
53
  }
52
54
 
55
+ var err error
56
+ if *configPath != "" {
57
+ config, err = lint.ReadConfig(*configPath)
58
+ } else {
59
+ config, err = lint.ReadConfigFromWorkingDir()
60
+ }
61
+
62
+ if err != nil {
63
+ fmt.Fprintf(os.Stderr, "An error occured trying to read the config file: %s", err.Error())
64
+ os.Exit(1)
65
+ }
66
+
53
67
  if flag.NArg() == 0 {
54
68
  lintDir(".")
55
69
  } else {
@@ -164,7 +178,7 @@ func lintFiles(filenames ...string) {
164
178
  files[filename] = src
165
179
  }
166
180
 
167
- l := new(lint.Linter)
181
+ l := &lint.Linter{Config: config}
168
182
  ps, err := l.LintFiles(files)
169
183
  if err != nil {
170
184
  fmt.Fprintf(os.Stderr, "%v\n", err)
@@ -1,3 +1,3 @@
1
1
  package main
2
2
 
3
- const VERSION string = "0.0.3"
3
+ const VERSION string = "0.0.4"
@@ -1,3 +1,3 @@
1
1
  module Pikeman
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lint.go CHANGED
@@ -30,6 +30,7 @@ const styleGuideBase = "https://golang.org/wiki/CodeReviewComments"
30
30
 
31
31
  // A Linter lints Go source code.
32
32
  type Linter struct {
33
+ Config *Config
33
34
  }
34
35
 
35
36
  // Problem represents a problem in some source code.
@@ -86,6 +87,36 @@ func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) {
86
87
  fset: token.NewFileSet(),
87
88
  files: make(map[string]*file),
88
89
  }
90
+
91
+ rules := map[Rule]func(f *file){
92
+ l.Config.PackageComment: lintPackageComment,
93
+ l.Config.Imports: lintImports,
94
+ l.Config.BlankImports: lintBlankImports,
95
+ l.Config.Exported: lintExported,
96
+ l.Config.Names: lintNames,
97
+ l.Config.VarDecls: lintVarDecls,
98
+ l.Config.Elses: lintElses,
99
+ l.Config.IfError: lintIfError,
100
+ l.Config.Ranges: lintRanges,
101
+ l.Config.Errorf: lintErrorf,
102
+ l.Config.Errors: lintErrors,
103
+ l.Config.ErrorStrings: lintErrorStrings,
104
+ l.Config.ReceiverNames: lintReceiverNames,
105
+ l.Config.IncDec: lintIncDec,
106
+ l.Config.ErrorReturn: lintErrorReturn,
107
+ l.Config.UnexportedReturn: lintUnexportedReturn,
108
+ l.Config.TimeNames: lintTimeNames,
109
+ l.Config.ContextKeyTypes: lintContextKeyTypes,
110
+ l.Config.ContextArgs: lintContextArgs,
111
+ }
112
+
113
+ var enabledRules []func(f *file)
114
+ for rule, fn := range rules {
115
+ if rule.IsEnabled() {
116
+ enabledRules = append(enabledRules, fn)
117
+ }
118
+ }
119
+
89
120
  var pkgName string
90
121
  for filename, src := range files {
91
122
  if isGenerated(src) {
@@ -106,6 +137,7 @@ func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) {
106
137
  fset: pkg.fset,
107
138
  src: src,
108
139
  filename: filename,
140
+ rules: enabledRules,
109
141
  }
110
142
  }
111
143
  if len(pkg.files) == 0 {
@@ -187,30 +219,15 @@ type file struct {
187
219
  fset *token.FileSet
188
220
  src []byte
189
221
  filename string
222
+ rules []func(*file)
190
223
  }
191
224
 
192
225
  func (f *file) isTest() bool { return strings.HasSuffix(f.filename, "_test.go") }
193
226
 
194
227
  func (f *file) lint() {
195
- f.lintPackageComment()
196
- f.lintImports()
197
- f.lintBlankImports()
198
- f.lintExported()
199
- f.lintNames()
200
- f.lintVarDecls()
201
- f.lintElses()
202
- f.lintIfError()
203
- f.lintRanges()
204
- f.lintErrorf()
205
- f.lintErrors()
206
- f.lintErrorStrings()
207
- f.lintReceiverNames()
208
- f.lintIncDec()
209
- f.lintErrorReturn()
210
- f.lintUnexportedReturn()
211
- f.lintTimeNames()
212
- f.lintContextKeyTypes()
213
- f.lintContextArgs()
228
+ for _, rule := range f.rules {
229
+ rule(f)
230
+ }
214
231
  }
215
232
 
216
233
  type link string
@@ -377,7 +394,7 @@ func (f *file) isMain() bool {
377
394
  // This has a notable false positive in that a package comment
378
395
  // could rightfully appear in a different file of the same package,
379
396
  // but that's not easy to fix since this linter is file-oriented.
380
- func (f *file) lintPackageComment() {
397
+ func lintPackageComment(f *file) {
381
398
  if f.isTest() {
382
399
  return
383
400
  }
@@ -430,7 +447,7 @@ func (f *file) lintPackageComment() {
430
447
 
431
448
  // lintBlankImports complains if a non-main package has blank imports that are
432
449
  // not documented.
433
- func (f *file) lintBlankImports() {
450
+ func lintBlankImports(f *file) {
434
451
  // In package main and in tests, we don't complain about blank imports.
435
452
  if f.pkg.main || f.isTest() {
436
453
  return
@@ -461,7 +478,7 @@ func (f *file) lintBlankImports() {
461
478
  }
462
479
 
463
480
  // lintImports examines import blocks.
464
- func (f *file) lintImports() {
481
+ func lintImports(f *file) {
465
482
  for i, is := range f.f.Imports {
466
483
  _ = i
467
484
  if is.Name != nil && is.Name.Name == "." && !f.isTest() {
@@ -481,7 +498,7 @@ const docCommentsLink = styleGuideBase + "#doc-comments"
481
498
  // doc comments for constants to be on top of the const block.
482
499
  // It also complains if the names stutter when combined with
483
500
  // the package name.
484
- func (f *file) lintExported() {
501
+ func lintExported(f *file) {
485
502
  if f.isTest() {
486
503
  return
487
504
  }
@@ -542,7 +559,7 @@ var knownNameExceptions = map[string]bool{
542
559
 
543
560
  // lintNames examines all names in the file.
544
561
  // It complains if any use underscores or incorrect known initialisms.
545
- func (f *file) lintNames() {
562
+ func lintNames(f *file) {
546
563
  // Package names need slightly different handling than other names.
547
564
  if strings.Contains(f.f.Name.Name, "_") && !strings.HasSuffix(f.f.Name.Name, "_test") {
548
565
  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")
@@ -957,7 +974,7 @@ var zeroLiteral = map[string]bool{
957
974
 
958
975
  // lintVarDecls examines variable declarations. It complains about declarations with
959
976
  // redundant LHS types that can be inferred from the RHS.
960
- func (f *file) lintVarDecls() {
977
+ func lintVarDecls(f *file) {
961
978
  var lastGen *ast.GenDecl // last GenDecl entered.
962
979
 
963
980
  f.walk(func(node ast.Node) bool {
@@ -1034,7 +1051,7 @@ func validType(T types.Type) bool {
1034
1051
  }
1035
1052
 
1036
1053
  // lintElses examines else blocks. It complains about any else block whose if block ends in a return.
1037
- func (f *file) lintElses() {
1054
+ func lintElses(f *file) {
1038
1055
  // We don't want to flag if { } else if { } else { } constructions.
1039
1056
  // They will appear as an IfStmt whose Else field is also an IfStmt.
1040
1057
  // Record such a node so we ignore it when we visit it.
@@ -1078,7 +1095,7 @@ func (f *file) lintElses() {
1078
1095
  }
1079
1096
 
1080
1097
  // lintRanges examines range clauses. It complains about redundant constructions.
1081
- func (f *file) lintRanges() {
1098
+ func lintRanges(f *file) {
1082
1099
  f.walk(func(node ast.Node) bool {
1083
1100
  rs, ok := node.(*ast.RangeStmt)
1084
1101
  if !ok {
@@ -1109,7 +1126,7 @@ func (f *file) lintRanges() {
1109
1126
  }
1110
1127
 
1111
1128
  // lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation.
1112
- func (f *file) lintErrorf() {
1129
+ func lintErrorf(f *file) {
1113
1130
  f.walk(func(node ast.Node) bool {
1114
1131
  ce, ok := node.(*ast.CallExpr)
1115
1132
  if !ok || len(ce.Args) != 1 {
@@ -1147,7 +1164,7 @@ func (f *file) lintErrorf() {
1147
1164
  }
1148
1165
 
1149
1166
  // lintErrors examines global error vars. It complains if they aren't named in the standard way.
1150
- func (f *file) lintErrors() {
1167
+ func lintErrors(f *file) {
1151
1168
  for _, decl := range f.f.Decls {
1152
1169
  gd, ok := decl.(*ast.GenDecl)
1153
1170
  if !ok || gd.Tok != token.VAR {
@@ -1202,7 +1219,7 @@ func lintErrorString(s string) (isClean bool, conf float64) {
1202
1219
 
1203
1220
  // lintErrorStrings examines error strings.
1204
1221
  // It complains if they are capitalized or end in punctuation or a newline.
1205
- func (f *file) lintErrorStrings() {
1222
+ func lintErrorStrings(f *file) {
1206
1223
  f.walk(func(node ast.Node) bool {
1207
1224
  ce, ok := node.(*ast.CallExpr)
1208
1225
  if !ok {
@@ -1235,7 +1252,7 @@ func (f *file) lintErrorStrings() {
1235
1252
 
1236
1253
  // lintReceiverNames examines receiver names. It complains about inconsistent
1237
1254
  // names used for the same type and names such as "this".
1238
- func (f *file) lintReceiverNames() {
1255
+ func lintReceiverNames(f *file) {
1239
1256
  typeReceiver := map[string]string{}
1240
1257
  f.walk(func(n ast.Node) bool {
1241
1258
  fn, ok := n.(*ast.FuncDecl)
@@ -1268,7 +1285,7 @@ func (f *file) lintReceiverNames() {
1268
1285
 
1269
1286
  // lintIncDec examines statements that increment or decrement a variable.
1270
1287
  // It complains if they don't use x++ or x--.
1271
- func (f *file) lintIncDec() {
1288
+ func lintIncDec(f *file) {
1272
1289
  f.walk(func(n ast.Node) bool {
1273
1290
  as, ok := n.(*ast.AssignStmt)
1274
1291
  if !ok {
@@ -1296,7 +1313,7 @@ func (f *file) lintIncDec() {
1296
1313
 
1297
1314
  // lintErrorReturn examines function declarations that return an error.
1298
1315
  // It complains if the error isn't the last parameter.
1299
- func (f *file) lintErrorReturn() {
1316
+ func lintErrorReturn(f *file) {
1300
1317
  f.walk(func(n ast.Node) bool {
1301
1318
  fn, ok := n.(*ast.FuncDecl)
1302
1319
  if !ok || fn.Type.Results == nil {
@@ -1320,7 +1337,7 @@ func (f *file) lintErrorReturn() {
1320
1337
 
1321
1338
  // lintUnexportedReturn examines exported function declarations.
1322
1339
  // It complains if any return an unexported type.
1323
- func (f *file) lintUnexportedReturn() {
1340
+ func lintUnexportedReturn(f *file) {
1324
1341
  f.walk(func(n ast.Node) bool {
1325
1342
  fn, ok := n.(*ast.FuncDecl)
1326
1343
  if !ok {
@@ -1384,7 +1401,7 @@ var timeSuffixes = []string{
1384
1401
  "MS", "Ms",
1385
1402
  }
1386
1403
 
1387
- func (f *file) lintTimeNames() {
1404
+ func lintTimeNames(f *file) {
1388
1405
  f.walk(func(node ast.Node) bool {
1389
1406
  v, ok := node.(*ast.ValueSpec)
1390
1407
  if !ok {
@@ -1420,7 +1437,7 @@ func (f *file) lintTimeNames() {
1420
1437
  // lintContextKeyTypes checks for call expressions to context.WithValue with
1421
1438
  // basic types used for the key argument.
1422
1439
  // See: https://golang.org/issue/17293
1423
- func (f *file) lintContextKeyTypes() {
1440
+ func lintContextKeyTypes(f *file) {
1424
1441
  f.walk(func(node ast.Node) bool {
1425
1442
  switch node := node.(type) {
1426
1443
  case *ast.CallExpr:
@@ -1460,7 +1477,7 @@ func (f *file) checkContextKeyType(x *ast.CallExpr) {
1460
1477
  // lintContextArgs examines function declarations that contain an
1461
1478
  // argument with a type of context.Context
1462
1479
  // It complains if that argument isn't the first parameter.
1463
- func (f *file) lintContextArgs() {
1480
+ func lintContextArgs(f *file) {
1464
1481
  f.walk(func(n ast.Node) bool {
1465
1482
  fn, ok := n.(*ast.FuncDecl)
1466
1483
  if !ok || len(fn.Type.Params.List) <= 1 {
@@ -1500,7 +1517,7 @@ func (f *file) containsComments(start, end token.Pos) bool {
1500
1517
  return false
1501
1518
  }
1502
1519
 
1503
- func (f *file) lintIfError() {
1520
+ func lintIfError(f *file) {
1504
1521
  f.walk(func(node ast.Node) bool {
1505
1522
  switch v := node.(type) {
1506
1523
  case *ast.BlockStmt:
@@ -26,7 +26,8 @@ import (
26
26
  var lintMatch = flag.String("lint.match", "", "restrict testdata matches to this pattern")
27
27
 
28
28
  func TestAll(t *testing.T) {
29
- l := new(Linter)
29
+ config, _ := decodeConfig(bytes.NewBuffer(defaultConfig))
30
+ l := &Linter{Config: config}
30
31
  rx, err := regexp.Compile(*lintMatch)
31
32
  if err != nil {
32
33
  t.Fatalf("Bad -lint.match value %q: %v", *lintMatch, err)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pikeman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Bedard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-14 00:00:00.000000000 Z
11
+ date: 2018-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cli-kit
@@ -90,6 +90,7 @@ files:
90
90
  - bin/setup
91
91
  - build/darwin-amd64/pikeman
92
92
  - build/linux-amd64/pikeman
93
+ - config.go
93
94
  - golint/golint.go
94
95
  - golint/import.go
95
96
  - golint/version.go