1
0
mirror of https://github.com/mgechev/revive.git synced 2025-03-05 15:05:47 +02:00

Implement command line arguments

This commit is contained in:
mgechev 2018-01-27 16:22:17 -08:00
parent a227153bc2
commit 8746067321
8 changed files with 218 additions and 106 deletions

196
config.go Normal file
View File

@ -0,0 +1,196 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"strings"
zglob "github.com/mattn/go-zglob"
"github.com/mgechev/revive/formatter"
"github.com/BurntSushi/toml"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
var defaultRules = []lint.Rule{
&rule.VarDeclarationsRule{},
&rule.PackageCommentsRule{},
&rule.DotImportsRule{},
&rule.BlankImportsRule{},
&rule.ExportedRule{},
&rule.NamesRule{},
&rule.ElseRule{},
&rule.IfReturnRule{},
&rule.RangeRule{},
&rule.ErrorfRule{},
&rule.ErrorsRule{},
&rule.ErrorStringsRule{},
&rule.ReceiverNameRule{},
&rule.IncrementDecrementRule{},
&rule.ErrorReturnRule{},
&rule.UnexportedReturnRule{},
&rule.TimeNamesRule{},
&rule.ContextKeyTypeRule{},
&rule.ContextArgumentsRule{},
}
var allRules = append([]lint.Rule{
&rule.ArgumentsLimitRule{},
&rule.CyclomaticRule{},
}, defaultRules...)
var allFormatters = []lint.Formatter{
&formatter.CLIFormatter{},
&formatter.JSONFormatter{},
}
func getFormatters() map[string]lint.Formatter {
result := map[string]lint.Formatter{}
for _, f := range allFormatters {
result[f.Name()] = f
}
return result
}
func getLintingRules(config *lint.Config) []lint.Rule {
rulesMap := map[string]lint.Rule{}
for _, r := range allRules {
rulesMap[r.Name()] = r
}
lintingRules := []lint.Rule{}
for name := range config.Rules {
rule, ok := rulesMap[name]
if !ok {
panic("cannot find rule: " + name)
}
lintingRules = append(lintingRules, rule)
}
return lintingRules
}
func parseConfig(path string) *lint.Config {
config := &lint.Config{}
file, err := ioutil.ReadFile(path)
if err != nil {
panic("cannot read the config file")
}
_, err = toml.Decode(string(file), config)
if err != nil {
panic("cannot parse the config file: " + err.Error())
}
return config
}
func normalizeConfig(config *lint.Config) {
severity := config.Severity
if severity != "" {
for k, v := range config.Rules {
if v.Severity == "" {
v.Severity = severity
}
config.Rules[k] = v
}
}
}
func getConfig() *lint.Config {
config := defaultConfig()
if configPath != "" {
config = parseConfig(configPath)
}
normalizeConfig(config)
return config
}
func getFormatter() lint.Formatter {
formatters := getFormatters()
formatter := formatters["cli"]
if formatterName != "" {
f, ok := formatters[formatterName]
if !ok {
panic("unknown formatter " + formatterName)
}
formatter = f
}
return formatter
}
func defaultConfig() *lint.Config {
defaultConfig := lint.Config{
Confidence: 0.0,
Severity: lint.SeverityWarning,
Rules: map[string]lint.RuleConfig{},
}
for _, r := range defaultRules {
defaultConfig.Rules[r.Name()] = lint.RuleConfig{}
}
return &defaultConfig
}
func getFiles() []string {
globs := flag.Args()
if len(globs) == 0 {
panic("files not specified")
}
var matches []string
for _, g := range globs {
m, err := zglob.Glob(g)
if err != nil {
panic(err)
}
matches = append(matches, m...)
}
if excludeGlobs == "" {
return matches
}
excluded := map[string]bool{}
excludeGlobSlice := strings.Split(excludeGlobs, " ")
for _, g := range excludeGlobSlice {
m, err := zglob.Glob(g)
if err != nil {
panic("error while parsing glob from exclude " + err.Error())
}
for _, match := range m {
excluded[match] = true
}
}
var finalMatches []string
for _, m := range matches {
if _, ok := excluded[m]; !ok {
finalMatches = append(finalMatches, m)
}
}
return finalMatches
}
var configPath string
var excludeGlobs string
var formatterName string
var help bool
var originalUsage = flag.Usage
func init() {
flag.Usage = func() {
fmt.Println(banner)
originalUsage()
}
const (
configUsage = "path to the configuration TOML file"
excludeUsage = "glob which specifies files to be excluded"
formatterUsage = "formatter to be used for the output"
)
flag.StringVar(&configPath, "config", "", configUsage)
flag.StringVar(&excludeGlobs, "exclude", "", excludeUsage)
flag.StringVar(&formatterName, "formatter", "", formatterUsage)
flag.Parse()
}

View File

@ -2,9 +2,9 @@ severity = "warning"
confidence = 0.8 confidence = 0.8
[rule.else] [rule.else]
severity = "error"
[rule.names] [rule.names]
[rule.argument-limit] [rule.argument-limit]
arguments = [2] arguments = [2]
[rule.cyclomatic] [rule.cyclomatic]
severity = "error"
arguments = [3] arguments = [3]

View File

@ -1,7 +1,7 @@
// Test for returning errors. // Test for returning errors.
// Package foo ... // Package foo ...
package foo package pkg
// Returns nothing // Returns nothing
func f() { // ok func f() { // ok

View File

@ -20,6 +20,11 @@ type CLIFormatter struct {
Metadata lint.FormatterMetadata Metadata lint.FormatterMetadata
} }
// Name returns the name of the formatter
func (f *CLIFormatter) Name() string {
return "cli"
}
func formatFailure(failure lint.Failure, severity lint.Severity) []string { func formatFailure(failure lint.Failure, severity lint.Severity) []string {
fString := color.BlueString(failure.Failure) fString := color.BlueString(failure.Failure)
fName := color.RedString(failure.RuleName) fName := color.RedString(failure.RuleName)

View File

@ -12,6 +12,11 @@ type JSONFormatter struct {
Metadata lint.FormatterMetadata Metadata lint.FormatterMetadata
} }
// Name returns the name of the formatter
func (f *JSONFormatter) Name() string {
return "json"
}
// Format formats the failures gotten from the lint. // Format formats the failures gotten from the lint.
func (f *JSONFormatter) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) { func (f *JSONFormatter) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
var slice []lint.Failure var slice []lint.Failure

View File

@ -9,5 +9,6 @@ type FormatterMetadata struct {
// Formatter defines an interface for failure formatters // Formatter defines an interface for failure formatters
type Formatter interface { type Formatter interface {
Format(<-chan Failure, RulesConfig) string Format(<-chan Failure, RulesConfig) (string, error)
Name() string
} }

110
main.go
View File

@ -5,122 +5,29 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/BurntSushi/toml"
"github.com/mgechev/revive/formatter"
"github.com/mgechev/revive/lint" "github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
) )
var allRules = []lint.Rule{ const banner = `
&rule.ArgumentsLimitRule{}, Welcome to:
&rule.VarDeclarationsRule{}, _ __ _____ _(_)__ _____
&rule.PackageCommentsRule{},
&rule.DotImportsRule{},
&rule.BlankImportsRule{},
&rule.ExportedRule{},
&rule.NamesRule{},
&rule.ElseRule{},
&rule.IfReturnRule{},
&rule.RangeRule{},
&rule.ErrorfRule{},
&rule.ErrorsRule{},
&rule.ErrorStringsRule{},
&rule.ReceiverNameRule{},
&rule.IncrementDecrementRule{},
&rule.ErrorReturnRule{},
&rule.UnexportedReturnRule{},
&rule.TimeNamesRule{},
&rule.ContextKeyTypeRule{},
&rule.ContextArgumentsRule{},
&rule.CyclomaticRule{},
}
func getLintingRules(config *lint.Config) []lint.Rule {
rulesMap := map[string]lint.Rule{}
for _, r := range allRules {
rulesMap[r.Name()] = r
}
lintingRules := []lint.Rule{}
for name := range config.Rules {
rule, ok := rulesMap[name]
if !ok {
panic("cannot find rule: " + name)
}
lintingRules = append(lintingRules, rule)
}
return lintingRules
}
func parseConfig(path string) *lint.Config {
config := &lint.Config{}
file, err := ioutil.ReadFile(path)
if err != nil {
panic("cannot read the config file")
}
_, err = toml.Decode(string(file), config)
if err != nil {
panic("cannot parse the config file: " + err.Error())
}
return config
}
func normalizeConfig(config *lint.Config) {
severity := config.Severity
if severity != "" {
for k, v := range config.Rules {
if v.Severity == "" {
v.Severity = severity
}
config.Rules[k] = v
}
}
}
const usage = `
Welcome to:
_ __ _____ _(_)__ _____
| '__/ _ \ \ / / \ \ / / _ \ | '__/ _ \ \ / / \ \ / / _ \
| | | __/\ V /| |\ V / __/ | | | __/\ V /| |\ V / __/
|_| \___| \_/ |_| \_/ \___| |_| \___| \_/ |_| \_/ \___|
Usage:
revive [flags] <Go file or directory> ...
Flags:
-c string path to the configuration TOML file.
-e string glob which specifies files to be excluded.
-f string formatter to be used for the output.
-h output this screen.
` `
func main() { func main() {
src := ` config := getConfig()
package p formatter := getFormatter()
files := getFiles()
// revive:disable:cyclomatic
func Test() {
if true || bar && baz {
return 42;
} else {
return 23;
}
}
// revive:enable:cyclomatic
func foo_bar(a int, b int, c int, d int) {
return a + b + c;
}`
revive := lint.New(func(file string) ([]byte, error) { revive := lint.New(func(file string) ([]byte, error) {
return []byte(src), nil return ioutil.ReadFile(file)
}) })
config := parseConfig("config.toml")
normalizeConfig(config)
lintingRules := getLintingRules(config) lintingRules := getLintingRules(config)
failures, err := revive.Lint([]string{"foo.go", "bar.go", "baz.go"}, lintingRules, config.Rules) failures, err := revive.Lint(files, lintingRules, config.Rules)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -130,7 +37,6 @@ func main() {
var output string var output string
go (func() { go (func() {
var formatter formatter.CLIFormatter
output, err = formatter.Format(formatChan, config.Rules) output, err = formatter.Format(formatChan, config.Rules)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -143,7 +143,6 @@ func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, ru
ps, err := l.Lint([]string{fi.Name()}, rules, config) ps, err := l.Lint([]string{fi.Name()}, rules, config)
if err != nil { if err != nil {
return err return err
} }