mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-26 17:08:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			326 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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/mgechev/dots"
 | |
| 	"github.com/mgechev/revive/formatter"
 | |
| 	"github.com/mgechev/revive/lint"
 | |
| 	"github.com/mgechev/revive/rule"
 | |
| 	"github.com/mitchellh/go-homedir"
 | |
| 	"github.com/pelletier/go-toml"
 | |
| )
 | |
| 
 | |
| 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.Unmarshal(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)
 | |
| }
 |