1
0
mirror of https://github.com/mgechev/revive.git synced 2024-11-28 08:49:11 +02:00

Improve design

This commit is contained in:
mgechev 2018-01-21 18:27:32 -08:00
parent 98dce265c6
commit f336ff920d
3 changed files with 170 additions and 162 deletions

View File

@ -4,6 +4,9 @@ import (
"go/ast"
"go/parser"
"go/token"
"math"
"regexp"
"strings"
)
// File abstraction used for representing files.
@ -44,3 +47,160 @@ func (f *File) isMain() bool {
}
return false
}
func (f *File) lint(rules []Rule, rulesConfig RulesConfig) []Failure {
var failures []Failure
disabledIntervals := f.disabledIntervals(rules)
for _, currentRule := range rules {
config := rulesConfig[currentRule.Name()]
currentFailures := currentRule.Apply(f, config)
for idx, failure := range currentFailures {
if failure.RuleName == "" {
failure.RuleName = currentRule.Name()
}
if failure.Node != nil {
failure.Position = ToFailurePosition(failure.Node.Pos(), failure.Node.End(), f)
}
currentFailures[idx] = failure
}
currentFailures = f.filterFailures(currentFailures, disabledIntervals)
failures = append(failures, currentFailures...)
}
return failures
}
type enableDisableConfig struct {
enabled bool
position int
}
func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
re := regexp.MustCompile(`^\s*revive:(enable|disable)(?:-(line|next-line))?(:|\s|$)`)
enabledDisabledRulesMap := make(map[string][]enableDisableConfig)
getEnabledDisabledIntervals := func() disabledIntervalsMap {
result := make(disabledIntervalsMap)
for ruleName, disabledArr := range enabledDisabledRulesMap {
ruleResult := []DisabledInterval{}
for i := 0; i < len(disabledArr); i++ {
interval := DisabledInterval{
RuleName: ruleName,
From: token.Position{
Filename: f.Name,
Line: disabledArr[i].position,
},
To: token.Position{
Filename: f.Name,
Line: math.MaxInt32,
},
}
if i%2 == 0 {
ruleResult = append(ruleResult, interval)
} else {
ruleResult[len(ruleResult)-1].To.Line = disabledArr[i].position
}
}
result[ruleName] = ruleResult
}
return result
}
handleConfig := func(isEnabled bool, line int, name string) {
existing, ok := enabledDisabledRulesMap[name]
if !ok {
existing = []enableDisableConfig{}
enabledDisabledRulesMap[name] = existing
}
if (len(existing) > 1 && existing[len(existing)-1].enabled == isEnabled) ||
(len(existing) == 0 && isEnabled) {
return
}
existing = append(existing, enableDisableConfig{
enabled: isEnabled,
position: line,
})
enabledDisabledRulesMap[name] = existing
}
handleRules := func(filename, modifier string, isEnabled bool, line int, ruleNames []string) []DisabledInterval {
var result []DisabledInterval
for _, name := range ruleNames {
if modifier == "line" {
handleConfig(isEnabled, line, name)
handleConfig(!isEnabled, line, name)
} else if modifier == "next-line" {
handleConfig(isEnabled, line+1, name)
handleConfig(!isEnabled, line+1, name)
} else {
handleConfig(isEnabled, line, name)
}
}
return result
}
handleComment := func(filename string, c *ast.CommentGroup, line int) {
text := c.Text()
parts := re.FindStringSubmatch(text)
if len(parts) == 0 {
return
}
str := re.FindString(text)
ruleNamesString := strings.Split(text, str)
ruleNames := []string{}
if len(ruleNamesString) == 2 {
tempNames := strings.Split(ruleNamesString[1], ",")
for _, name := range tempNames {
name = strings.Trim(name, "\n")
if len(name) > 0 {
ruleNames = append(ruleNames, name)
}
}
}
// TODO: optimize
if len(ruleNames) == 0 {
for _, rule := range rules {
ruleNames = append(ruleNames, rule.Name())
}
}
handleRules(filename, parts[2], parts[1] == "enable", line, ruleNames)
}
comments := f.GetAST().Comments
for _, c := range comments {
handleComment(f.Name, c, f.ToPosition(c.Pos()).Line)
}
return getEnabledDisabledIntervals()
}
func (f *File) filterFailures(failures []Failure, disabledIntervals disabledIntervalsMap) []Failure {
result := []Failure{}
for _, failure := range failures {
fStart := failure.Position.Start.Line
fEnd := failure.Position.End.Line
intervals, ok := disabledIntervals[failure.RuleName]
if !ok {
result = append(result, failure)
} else {
include := true
for _, interval := range intervals {
intStart := interval.From.Line
intEnd := interval.To.Line
if (fStart >= intStart && fStart <= intEnd) ||
(fEnd >= intStart && fEnd <= intEnd) {
include = false
break
}
}
if include {
result = append(result, failure)
}
}
}
return result
}

View File

@ -4,11 +4,7 @@ import (
"bufio"
"bytes"
"fmt"
"go/ast"
"go/token"
"math"
"regexp"
"strings"
)
// ReadFile defines an abstraction for reading files.
@ -47,11 +43,6 @@ func isGenerated(src []byte) bool {
// Lint lints a set of files with the specified rule.
func (l *Linter) Lint(filenames []string, ruleSet []Rule, rulesConfig RulesConfig) ([]Failure, error) {
var failures []Failure
ruleNames := []string{}
for _, r := range ruleSet {
ruleNames = append(ruleNames, r.Name())
}
pkg := &Package{
Fset: token.NewFileSet(),
Files: map[string]*File{},
@ -78,159 +69,7 @@ func (l *Linter) Lint(filenames []string, ruleSet []Rule, rulesConfig RulesConfi
}
pkg.Files[filename] = file
disabledIntervals := l.disabledIntervals(file, ruleNames)
pkg.TypeCheck()
for _, currentRule := range ruleSet {
config := rulesConfig[currentRule.Name()]
currentFailures := currentRule.Apply(file, config)
for idx, failure := range currentFailures {
if failure.RuleName == "" {
failure.RuleName = currentRule.Name()
}
if failure.Node != nil {
failure.Position = ToFailurePosition(failure.Node.Pos(), failure.Node.End(), file)
}
currentFailures[idx] = failure
}
currentFailures = l.filterFailures(currentFailures, disabledIntervals)
failures = append(failures, currentFailures...)
}
}
return failures, nil
}
type enableDisableConfig struct {
enabled bool
position int
}
func (l *Linter) disabledIntervals(file *File, allRuleNames []string) disabledIntervalsMap {
re := regexp.MustCompile(`^\s*revive:(enable|disable)(?:-(line|next-line))?(:|\s|$)`)
enabledDisabledRulesMap := make(map[string][]enableDisableConfig)
getEnabledDisabledIntervals := func() disabledIntervalsMap {
result := make(disabledIntervalsMap)
for ruleName, disabledArr := range enabledDisabledRulesMap {
ruleResult := []DisabledInterval{}
for i := 0; i < len(disabledArr); i++ {
interval := DisabledInterval{
RuleName: ruleName,
From: token.Position{
Filename: file.Name,
Line: disabledArr[i].position,
},
To: token.Position{
Filename: file.Name,
Line: math.MaxInt32,
},
}
if i%2 == 0 {
ruleResult = append(ruleResult, interval)
} else {
ruleResult[len(ruleResult)-1].To.Line = disabledArr[i].position
}
}
result[ruleName] = ruleResult
}
return result
}
handleConfig := func(isEnabled bool, line int, name string) {
existing, ok := enabledDisabledRulesMap[name]
if !ok {
existing = []enableDisableConfig{}
enabledDisabledRulesMap[name] = existing
}
if (len(existing) > 1 && existing[len(existing)-1].enabled == isEnabled) ||
(len(existing) == 0 && isEnabled) {
return
}
existing = append(existing, enableDisableConfig{
enabled: isEnabled,
position: line,
})
enabledDisabledRulesMap[name] = existing
}
handleRules := func(filename, modifier string, isEnabled bool, line int, ruleNames []string) []DisabledInterval {
var result []DisabledInterval
for _, name := range ruleNames {
if modifier == "line" {
handleConfig(isEnabled, line, name)
handleConfig(!isEnabled, line, name)
} else if modifier == "next-line" {
handleConfig(isEnabled, line+1, name)
handleConfig(!isEnabled, line+1, name)
} else {
handleConfig(isEnabled, line, name)
}
}
return result
}
handleComment := func(filename string, c *ast.CommentGroup, line int) {
text := c.Text()
parts := re.FindStringSubmatch(text)
if len(parts) == 0 {
return
}
str := re.FindString(text)
ruleNamesString := strings.Split(text, str)
ruleNames := []string{}
if len(ruleNamesString) == 2 {
tempNames := strings.Split(ruleNamesString[1], ",")
for _, name := range tempNames {
name = strings.Trim(name, "\n")
if len(name) > 0 {
ruleNames = append(ruleNames, name)
}
}
}
if len(ruleNames) == 0 {
ruleNames = allRuleNames
}
handleRules(filename, parts[2], parts[1] == "enable", line, ruleNames)
}
comments := file.GetAST().Comments
for _, c := range comments {
handleComment(file.Name, c, file.ToPosition(c.Pos()).Line)
}
return getEnabledDisabledIntervals()
}
func (l *Linter) filterFailures(failures []Failure, disabledIntervals disabledIntervalsMap) []Failure {
result := []Failure{}
for _, failure := range failures {
fStart := failure.Position.Start.Line
fEnd := failure.Position.End.Line
intervals, ok := disabledIntervals[failure.RuleName]
if !ok {
result = append(result, failure)
} else {
include := true
for _, interval := range intervals {
intStart := interval.From.Line
intEnd := interval.To.Line
if (fStart >= intStart && fStart <= intEnd) ||
(fEnd >= intStart && fEnd <= intEnd) {
include = false
break
}
}
if include {
result = append(result, failure)
}
}
}
return result
return pkg.lint(ruleSet, rulesConfig), nil
}

View File

@ -75,3 +75,12 @@ func (p *Package) TypeCheck() error {
p.TypesInfo = info
return err
}
func (p *Package) lint(rules []Rule, config RulesConfig) []Failure {
var failures []Failure
p.TypeCheck()
for _, file := range p.Files {
failures = append(failures, file.lint(rules, config)...)
}
return failures
}