1
0
mirror of https://github.com/mgechev/revive.git synced 2025-04-13 11:30:36 +02:00

Add extra rule

This commit is contained in:
mgechev 2018-02-04 14:51:19 -08:00
parent bd04af7465
commit b5fc0eaffc
10 changed files with 201 additions and 34 deletions

View File

@ -0,0 +1,4 @@
// baz baz qux
// foobar
package fixtures

View File

@ -0,0 +1,4 @@
/* baz baz qux
foobar */
package fixtures

View File

@ -0,0 +1,4 @@
// baz baz qux
// qux foo
package fixtures // MATCH /the file doesn't have an appropriate header/

View File

@ -0,0 +1,3 @@
// foobar
package fixtures

View File

@ -0,0 +1,4 @@
// foobaz qux
// bar
package fixtures

86
rule/file-header.go Normal file
View File

@ -0,0 +1,86 @@
package rule
import (
"fmt"
"go/ast"
"regexp"
"github.com/mgechev/revive/lint"
)
// FileHeaderRule lints given else constructs.
type FileHeaderRule struct{}
// Apply applies the rule to given file.
func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var failures []lint.Failure
header, ok := arguments[0].(string)
if !ok {
panic("Invalid argument to the FileHeaderRule")
}
regex, err := regexp.Compile(header)
if err != nil {
panic(err.Error())
}
fileAst := file.AST
walker := lintFileHeader{
file: file,
fileAst: fileAst,
regex: regex,
onFailure: func(failure lint.Failure) {
failures = append(failures, failure)
},
}
ast.Walk(walker, fileAst)
return failures
}
// Name returns the rule name.
func (r *FileHeaderRule) Name() string {
return "file-header"
}
type lintFileHeader struct {
file *lint.File
fileAst *ast.File
regex *regexp.Regexp
onFailure func(lint.Failure)
}
func (w lintFileHeader) Visit(n ast.Node) ast.Visitor {
g := w.fileAst.Comments[0]
failure := lint.Failure{
Node: w.fileAst,
Confidence: 1,
Failure: "the file doesn't have an appropriate header",
}
if g == nil {
w.onFailure(failure)
return nil
}
multi := regexp.MustCompile("^/\\*")
single := regexp.MustCompile("^//")
comment := ""
for _, c := range g.List {
text := c.Text
if multi.Match([]byte(text)) {
text = text[2 : len(text)-2]
} else if single.Match([]byte(text)) {
text = text[2:]
}
comment += text
}
fmt.Println("#############")
fmt.Println(comment)
fmt.Println("#############")
if !w.regex.Match([]byte(comment)) {
w.onFailure(failure)
}
return nil
}

17
test/cyclomatic_test.go Normal file
View File

@ -0,0 +1,17 @@
package test
import (
"testing"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
func TestCyclomatic(t *testing.T) {
testRule(t, "cyclomatic", &rule.CyclomaticRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(1)},
})
testRule(t, "cyclomatic-2", &rule.CyclomaticRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(3)},
})
}

30
test/file-header_test.go Normal file
View File

@ -0,0 +1,30 @@
package test
import (
"testing"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
func TestLintFileHeader(t *testing.T) {
testRule(t, "lint-file-header1", &rule.FileHeaderRule{}, &lint.RuleConfig{
Arguments: []interface{}{"foobar"},
})
testRule(t, "lint-file-header2", &rule.FileHeaderRule{}, &lint.RuleConfig{
Arguments: []interface{}{"foobar"},
})
testRule(t, "lint-file-header3", &rule.FileHeaderRule{}, &lint.RuleConfig{
Arguments: []interface{}{"foobar"},
})
testRule(t, "lint-file-header4", &rule.FileHeaderRule{}, &lint.RuleConfig{
Arguments: []interface{}{"^\\sfoobar$"},
})
testRule(t, "lint-file-header5", &rule.FileHeaderRule{}, &lint.RuleConfig{
Arguments: []interface{}{"^\\sfoo.*bar$"},
})
}

View File

@ -1,12 +1,4 @@
// Copyright (c) 2013 The Go Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd.
// The code contains changes from the original source.
package testutil
package test
import (
"bufio"
@ -56,21 +48,6 @@ var rules = []lint.Rule{
&rule.ContextArgumentsRule{},
}
func TestCyclomatic(t *testing.T) {
testRule(t, "cyclomatic", &rule.CyclomaticRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(1)},
})
testRule(t, "cyclomatic-2", &rule.CyclomaticRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(3)},
})
}
func TestMaxPublicStructs(t *testing.T) {
testRule(t, "max-public-structs", &rule.MaxPublicStructsRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(1)},
})
}
func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.RuleConfig) {
baseDir := "../fixtures/"
filename = filename + ".go"
@ -82,17 +59,14 @@ func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.Rul
if err != nil {
t.Fatalf("Cannot get file info for %s: %v", rule.Name(), err)
}
ins := parseInstructions(t, filename, src)
if ins == nil {
t.Errorf("Test file %v does not have instructions", filename)
return
}
if config == nil {
assertFailures(t, baseDir, stat, src, []lint.Rule{rule}, map[string]lint.RuleConfig{})
return
}
c := map[string]lint.RuleConfig{}
c[rule.Name()] = *config[0]
if config != nil {
c[rule.Name()] = *config[0]
}
if parseInstructions(t, filename, src) == nil {
assertSuccess(t, baseDir, stat, src, []lint.Rule{rule}, c)
return
}
assertFailures(t, baseDir, stat, src, []lint.Rule{rule}, c)
}
@ -103,6 +77,11 @@ func TestAll(t *testing.T) {
"cyclomatic.go": true,
"cyclomatic-2.go": true,
"max-public-structs.go": true,
"lint-file-header1.go": true,
"lint-file-header2.go": true,
"lint-file-header3.go": true,
"lint-file-header4.go": true,
"lint-file-header5.go": true,
}
rx, err := regexp.Compile(*lintMatch)
@ -138,6 +117,28 @@ func TestAll(t *testing.T) {
}
}
func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error {
l := lint.New(func(file string) ([]byte, error) {
return ioutil.ReadFile(baseDir + file)
})
ps, err := l.Lint([]string{fi.Name()}, rules, lint.Config{
Rules: config,
})
if err != nil {
return err
}
failures := ""
for p := range ps {
failures += p.Failure
}
if failures != "" {
t.Errorf("Expected the rule to pass but got the following failures: %s", failures)
}
return nil
}
func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error {
l := lint.New(func(file string) ([]byte, error) {
return ioutil.ReadFile(baseDir + file)

View File

@ -0,0 +1,14 @@
package test
import (
"testing"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
func TestMaxPublicStructs(t *testing.T) {
testRule(t, "max-public-structs", &rule.MaxPublicStructsRule{}, &lint.RuleConfig{
Arguments: []interface{}{int64(1)},
})
}