mirror of
https://github.com/mgechev/revive.git
synced 2025-04-21 11:56:55 +02:00
Improve design
This commit is contained in:
parent
98dce265c6
commit
f336ff920d
160
linter/file.go
160
linter/file.go
@ -4,6 +4,9 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// File abstraction used for representing files.
|
// File abstraction used for representing files.
|
||||||
@ -44,3 +47,160 @@ func (f *File) isMain() bool {
|
|||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
163
linter/linter.go
163
linter/linter.go
@ -4,11 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"math"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadFile defines an abstraction for reading files.
|
// 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.
|
// Lint lints a set of files with the specified rule.
|
||||||
func (l *Linter) Lint(filenames []string, ruleSet []Rule, rulesConfig RulesConfig) ([]Failure, error) {
|
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{
|
pkg := &Package{
|
||||||
Fset: token.NewFileSet(),
|
Fset: token.NewFileSet(),
|
||||||
Files: map[string]*File{},
|
Files: map[string]*File{},
|
||||||
@ -78,159 +69,7 @@ func (l *Linter) Lint(filenames []string, ruleSet []Rule, rulesConfig RulesConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkg.Files[filename] = file
|
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
|
return pkg.lint(ruleSet, rulesConfig), 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
|
|
||||||
}
|
}
|
||||||
|
@ -75,3 +75,12 @@ func (p *Package) TypeCheck() error {
|
|||||||
p.TypesInfo = info
|
p.TypesInfo = info
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user