Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
7.0 KiB
325 lines
7.0 KiB
// Copyright 2020 The Gitea Authors. All rights reserved. |
|
// Copyright (c) 2018 Minko Gechev. All rights reserved. |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
// +build ignore |
|
|
|
package main |
|
|
|
import ( |
|
"flag" |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"path/filepath" |
|
"strings" |
|
|
|
"github.com/BurntSushi/toml" |
|
"github.com/mgechev/dots" |
|
"github.com/mgechev/revive/formatter" |
|
"github.com/mgechev/revive/lint" |
|
"github.com/mgechev/revive/rule" |
|
"github.com/mitchellh/go-homedir" |
|
) |
|
|
|
func fail(err string) { |
|
fmt.Fprintln(os.Stderr, err) |
|
os.Exit(1) |
|
} |
|
|
|
var defaultRules = []lint.Rule{ |
|
&rule.VarDeclarationsRule{}, |
|
&rule.PackageCommentsRule{}, |
|
&rule.DotImportsRule{}, |
|
&rule.BlankImportsRule{}, |
|
&rule.ExportedRule{}, |
|
&rule.VarNamingRule{}, |
|
&rule.IndentErrorFlowRule{}, |
|
&rule.IfReturnRule{}, |
|
&rule.RangeRule{}, |
|
&rule.ErrorfRule{}, |
|
&rule.ErrorNamingRule{}, |
|
&rule.ErrorStringsRule{}, |
|
&rule.ReceiverNamingRule{}, |
|
&rule.IncrementDecrementRule{}, |
|
&rule.ErrorReturnRule{}, |
|
&rule.UnexportedReturnRule{}, |
|
&rule.TimeNamingRule{}, |
|
&rule.ContextKeysType{}, |
|
&rule.ContextAsArgumentRule{}, |
|
} |
|
|
|
var allRules = append([]lint.Rule{ |
|
&rule.ArgumentsLimitRule{}, |
|
&rule.CyclomaticRule{}, |
|
&rule.FileHeaderRule{}, |
|
&rule.EmptyBlockRule{}, |
|
&rule.SuperfluousElseRule{}, |
|
&rule.ConfusingNamingRule{}, |
|
&rule.GetReturnRule{}, |
|
&rule.ModifiesParamRule{}, |
|
&rule.ConfusingResultsRule{}, |
|
&rule.DeepExitRule{}, |
|
&rule.UnusedParamRule{}, |
|
&rule.UnreachableCodeRule{}, |
|
&rule.AddConstantRule{}, |
|
&rule.FlagParamRule{}, |
|
&rule.UnnecessaryStmtRule{}, |
|
&rule.StructTagRule{}, |
|
&rule.ModifiesValRecRule{}, |
|
&rule.ConstantLogicalExprRule{}, |
|
&rule.BoolLiteralRule{}, |
|
&rule.RedefinesBuiltinIDRule{}, |
|
&rule.ImportsBlacklistRule{}, |
|
&rule.FunctionResultsLimitRule{}, |
|
&rule.MaxPublicStructsRule{}, |
|
&rule.RangeValInClosureRule{}, |
|
&rule.RangeValAddress{}, |
|
&rule.WaitGroupByValueRule{}, |
|
&rule.AtomicRule{}, |
|
&rule.EmptyLinesRule{}, |
|
&rule.LineLengthLimitRule{}, |
|
&rule.CallToGCRule{}, |
|
&rule.DuplicatedImportsRule{}, |
|
&rule.ImportShadowingRule{}, |
|
&rule.BareReturnRule{}, |
|
&rule.UnusedReceiverRule{}, |
|
&rule.UnhandledErrorRule{}, |
|
&rule.CognitiveComplexityRule{}, |
|
&rule.StringOfIntRule{}, |
|
}, defaultRules...) |
|
|
|
var allFormatters = []lint.Formatter{ |
|
&formatter.Stylish{}, |
|
&formatter.Friendly{}, |
|
&formatter.JSON{}, |
|
&formatter.NDJSON{}, |
|
&formatter.Default{}, |
|
&formatter.Unix{}, |
|
&formatter.Checkstyle{}, |
|
&formatter.Plain{}, |
|
} |
|
|
|
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 { |
|
fail("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 { |
|
fail("cannot read the config file") |
|
} |
|
_, err = toml.Decode(string(file), config) |
|
if err != nil { |
|
fail("cannot parse the config file: " + err.Error()) |
|
} |
|
return config |
|
} |
|
|
|
func normalizeConfig(config *lint.Config) { |
|
if config.Confidence == 0 { |
|
config.Confidence = 0.8 |
|
} |
|
severity := config.Severity |
|
if severity != "" { |
|
for k, v := range config.Rules { |
|
if v.Severity == "" { |
|
v.Severity = severity |
|
} |
|
config.Rules[k] = v |
|
} |
|
for k, v := range config.Directives { |
|
if v.Severity == "" { |
|
v.Severity = severity |
|
} |
|
config.Directives[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["default"] |
|
if formatterName != "" { |
|
f, ok := formatters[formatterName] |
|
if !ok { |
|
fail("unknown formatter " + formatterName) |
|
} |
|
formatter = f |
|
} |
|
return formatter |
|
} |
|
|
|
func buildDefaultConfigPath() string { |
|
var result string |
|
if homeDir, err := homedir.Dir(); err == nil { |
|
result = filepath.Join(homeDir, "revive.toml") |
|
if _, err := os.Stat(result); err != nil { |
|
result = "" |
|
} |
|
} |
|
|
|
return result |
|
} |
|
|
|
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 normalizeSplit(strs []string) []string { |
|
res := []string{} |
|
for _, s := range strs { |
|
t := strings.Trim(s, " \t") |
|
if len(t) > 0 { |
|
res = append(res, t) |
|
} |
|
} |
|
return res |
|
} |
|
|
|
func getPackages() [][]string { |
|
globs := normalizeSplit(flag.Args()) |
|
if len(globs) == 0 { |
|
globs = append(globs, ".") |
|
} |
|
|
|
packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePaths)) |
|
if err != nil { |
|
fail(err.Error()) |
|
} |
|
|
|
return packages |
|
} |
|
|
|
type arrayFlags []string |
|
|
|
func (i *arrayFlags) String() string { |
|
return strings.Join([]string(*i), " ") |
|
} |
|
|
|
func (i *arrayFlags) Set(value string) error { |
|
*i = append(*i, value) |
|
return nil |
|
} |
|
|
|
var configPath string |
|
var excludePaths arrayFlags |
|
var formatterName string |
|
var help bool |
|
|
|
var originalUsage = flag.Usage |
|
|
|
func init() { |
|
flag.Usage = func() { |
|
originalUsage() |
|
} |
|
// command line help strings |
|
const ( |
|
configUsage = "path to the configuration TOML file, defaults to $HOME/revive.toml, if present (i.e. -config myconf.toml)" |
|
excludeUsage = "list of globs which specify files to be excluded (i.e. -exclude foo/...)" |
|
formatterUsage = "formatter to be used for the output (i.e. -formatter stylish)" |
|
) |
|
|
|
defaultConfigPath := buildDefaultConfigPath() |
|
|
|
flag.StringVar(&configPath, "config", defaultConfigPath, configUsage) |
|
flag.Var(&excludePaths, "exclude", excludeUsage) |
|
flag.StringVar(&formatterName, "formatter", "", formatterUsage) |
|
flag.Parse() |
|
} |
|
|
|
func main() { |
|
config := getConfig() |
|
formatter := getFormatter() |
|
packages := getPackages() |
|
|
|
revive := lint.New(func(file string) ([]byte, error) { |
|
return ioutil.ReadFile(file) |
|
}) |
|
|
|
lintingRules := getLintingRules(config) |
|
|
|
failures, err := revive.Lint(packages, lintingRules, *config) |
|
if err != nil { |
|
fail(err.Error()) |
|
} |
|
|
|
formatChan := make(chan lint.Failure) |
|
exitChan := make(chan bool) |
|
|
|
var output string |
|
go (func() { |
|
output, err = formatter.Format(formatChan, *config) |
|
if err != nil { |
|
fail(err.Error()) |
|
} |
|
exitChan <- true |
|
})() |
|
|
|
exitCode := 0 |
|
for f := range failures { |
|
if f.Confidence < config.Confidence { |
|
continue |
|
} |
|
if exitCode == 0 { |
|
exitCode = config.WarningCode |
|
} |
|
if c, ok := config.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError { |
|
exitCode = config.ErrorCode |
|
} |
|
if c, ok := config.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError { |
|
exitCode = config.ErrorCode |
|
} |
|
|
|
formatChan <- f |
|
} |
|
|
|
close(formatChan) |
|
<-exitChan |
|
if output != "" { |
|
fmt.Println(output) |
|
} |
|
|
|
os.Exit(exitCode) |
|
}
|
|
|