mirror of
https://github.com/mgechev/revive.git
synced 2024-11-28 08:49:11 +02:00
318db94210
* Separating lib from cli * Renamed NewRevive to New * Added GetLintFailures helper function * Moved formatter to call to format since that's when it's needed * makes fields of Revive struct non-public * minor modifs in tests: remove unnamed constats * Added lint package management to lint command * README message for using revive as a library * README formatting * Removed unused method * Slightly improved wording in README * Handling format errors * Renaming file to better reflect intent * Refactoring pattern usage * README heads * renames excludePaths into excludePatterns Co-authored-by: Bernardo Heynemann <bernardo.heynemann@coinbase.com> Co-authored-by: chavacava <salvadorcavadini+github@gmail.com>
205 lines
4.4 KiB
Go
205 lines
4.4 KiB
Go
package revivelib
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/mgechev/dots"
|
|
"github.com/mgechev/revive/config"
|
|
"github.com/mgechev/revive/lint"
|
|
"github.com/mgechev/revive/logging"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Revive is responsible for running linters and formatters
|
|
// and returning a set of results.
|
|
type Revive struct {
|
|
config *lint.Config
|
|
lintingRules []lint.Rule
|
|
logger *log.Logger
|
|
maxOpenFiles int
|
|
}
|
|
|
|
// New creates a new instance of Revive lint runner.
|
|
func New(
|
|
conf *lint.Config,
|
|
setExitStatus bool,
|
|
maxOpenFiles int,
|
|
extraRules ...ExtraRule,
|
|
) (*Revive, error) {
|
|
log, err := logging.GetLogger()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "initializing revive - getting logger")
|
|
}
|
|
|
|
if setExitStatus {
|
|
conf.ErrorCode = 1
|
|
conf.WarningCode = 1
|
|
}
|
|
|
|
extraRuleInstances := make([]lint.Rule, len(extraRules))
|
|
for i, extraRule := range extraRules {
|
|
extraRuleInstances[i] = extraRule.Rule
|
|
|
|
ruleName := extraRule.Rule.Name()
|
|
|
|
_, isRuleAlreadyConfigured := conf.Rules[ruleName]
|
|
if !isRuleAlreadyConfigured {
|
|
conf.Rules[ruleName] = extraRule.DefaultConfig
|
|
}
|
|
}
|
|
|
|
lintingRules, err := config.GetLintingRules(conf, extraRuleInstances)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "initializing revive - gettint lint rules")
|
|
}
|
|
|
|
log.Println("Config loaded")
|
|
|
|
return &Revive{
|
|
logger: log,
|
|
config: conf,
|
|
lintingRules: lintingRules,
|
|
maxOpenFiles: maxOpenFiles,
|
|
}, nil
|
|
}
|
|
|
|
// Lint the included patterns, skipping excluded ones
|
|
func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
|
|
includePatterns := []string{}
|
|
excludePatterns := []string{}
|
|
|
|
for _, lintpkg := range patterns {
|
|
if lintpkg.IsExclude() {
|
|
excludePatterns = append(excludePatterns, lintpkg.GetPattern())
|
|
} else {
|
|
includePatterns = append(includePatterns, lintpkg.GetPattern())
|
|
}
|
|
}
|
|
|
|
if len(excludePatterns) == 0 { // if no excludes were set
|
|
excludePatterns = r.config.Exclude // use those from the configuration
|
|
}
|
|
|
|
packages, err := getPackages(includePatterns, excludePatterns)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "linting - getting packages")
|
|
}
|
|
|
|
revive := lint.New(func(file string) ([]byte, error) {
|
|
contents, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "reading file "+file)
|
|
}
|
|
|
|
return contents, nil
|
|
}, r.maxOpenFiles)
|
|
|
|
failures, err := revive.Lint(packages, r.lintingRules, *r.config)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "linting - retrieving failures channel")
|
|
}
|
|
|
|
return failures, nil
|
|
}
|
|
|
|
// Format gets the output for a given failures channel from Lint.
|
|
func (r *Revive) Format(
|
|
formatterName string,
|
|
failuresChan <-chan lint.Failure,
|
|
) (string, int, error) {
|
|
conf := r.config
|
|
formatChan := make(chan lint.Failure)
|
|
exitChan := make(chan bool)
|
|
|
|
formatter, err := config.GetFormatter(formatterName)
|
|
if err != nil {
|
|
return "", 0, errors.Wrap(err, "formatting - getting formatter")
|
|
}
|
|
|
|
var (
|
|
output string
|
|
formatErr error
|
|
)
|
|
|
|
go func() {
|
|
output, formatErr = formatter.Format(formatChan, *conf)
|
|
|
|
exitChan <- true
|
|
}()
|
|
|
|
exitCode := 0
|
|
|
|
for failure := range failuresChan {
|
|
if failure.Confidence < conf.Confidence {
|
|
continue
|
|
}
|
|
|
|
if exitCode == 0 {
|
|
exitCode = conf.WarningCode
|
|
}
|
|
|
|
if c, ok := conf.Rules[failure.RuleName]; ok && c.Severity == lint.SeverityError {
|
|
exitCode = conf.ErrorCode
|
|
}
|
|
|
|
if c, ok := conf.Directives[failure.RuleName]; ok && c.Severity == lint.SeverityError {
|
|
exitCode = conf.ErrorCode
|
|
}
|
|
|
|
formatChan <- failure
|
|
}
|
|
|
|
close(formatChan)
|
|
<-exitChan
|
|
|
|
if formatErr != nil {
|
|
return "", exitCode, errors.Wrap(err, "formatting")
|
|
}
|
|
|
|
return output, exitCode, nil
|
|
}
|
|
|
|
func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]string, error) {
|
|
globs := normalizeSplit(includePatterns)
|
|
if len(globs) == 0 {
|
|
globs = append(globs, ".")
|
|
}
|
|
|
|
packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePatterns))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "getting packages - resolving packages in dots")
|
|
}
|
|
|
|
return packages, nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// ArrayFlags type for string list.
|
|
type ArrayFlags []string
|
|
|
|
func (i *ArrayFlags) String() string {
|
|
return strings.Join([]string(*i), " ")
|
|
}
|
|
|
|
// Set value for array flags.
|
|
func (i *ArrayFlags) Set(value string) error {
|
|
*i = append(*i, value)
|
|
|
|
return nil
|
|
}
|