mirror of
https://github.com/mgechev/revive.git
synced 2024-11-21 17:16:40 +02:00
Conf reason rule disabling (#193)
* adds support for comments when enabling/disabling * adds config flag to require disabling reason * Update lint/file.go adds code fmt suggestion by @mgechev Co-Authored-By: Minko Gechev <mgechev@gmail.com> * moves regexp compilation out of the function fix typo in condition * adds support for comments when enabling/disabling * skips incomplete directives and generate a failure * adds _directive_ concept to cope with specify-disable-reason * adds doc gofmt * fixes severity is ignored
This commit is contained in:
parent
639585fb79
commit
55cfae63e9
26
README.md
26
README.md
@ -140,7 +140,7 @@ revive -config revive.toml -exclude file1.go -exclude file2.go -formatter friend
|
||||
- The output will be formatted with the `friendly` formatter
|
||||
- The linter will analyze `github.com/mgechev/revive` and the files in `package`
|
||||
|
||||
### Comment Annotations
|
||||
### Comment Directives
|
||||
|
||||
Using comments, you can disable the linter for the entire file or only range of lines:
|
||||
|
||||
@ -167,6 +167,28 @@ func Public() private {
|
||||
|
||||
This way, `revive` will not warn you for that you're returning an object of an unexported type, from an exported function.
|
||||
|
||||
You can document why you disable the linter by adding a trailing text in the directive, for example
|
||||
|
||||
```go
|
||||
//revive:disable Until the code is stable
|
||||
```
|
||||
```go
|
||||
//revive:disable:cyclomatic High complexity score but easy to understand
|
||||
```
|
||||
|
||||
You can also configure `revive` to enforce documenting linter disabling directives by adding
|
||||
|
||||
```toml
|
||||
[directive.specify-disable-reason]
|
||||
```
|
||||
|
||||
in the configuration. You can set the severity (defaults to _warning_) of the violation of this directive
|
||||
|
||||
```toml
|
||||
[directive.specify-disable-reason]
|
||||
severity = "error"
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
`revive` can be configured with a TOML file. Here's a sample configuration with explanation for the individual properties:
|
||||
@ -399,7 +421,7 @@ Each formatter needs to implement the following interface:
|
||||
|
||||
```go
|
||||
type Formatter interface {
|
||||
Format(<-chan Failure, RulesConfig) (string, error)
|
||||
Format(<-chan Failure, Config) (string, error)
|
||||
Name() string
|
||||
}
|
||||
```
|
||||
|
@ -142,6 +142,12 @@ func normalizeConfig(config *lint.Config) {
|
||||
}
|
||||
config.Rules[k] = v
|
||||
}
|
||||
for k, v := range config.Directives {
|
||||
if v.Severity == "" {
|
||||
v.Severity = severity
|
||||
}
|
||||
config.Directives[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,13 @@ func foo1() {
|
||||
var invalid_name2 = 1 //revive:disable-line:var-naming
|
||||
}
|
||||
|
||||
func foo11() {
|
||||
//revive:disable-next-line:var-naming I'm an Eiffel programmer thus I like underscores
|
||||
var invalid_name = 0
|
||||
var invalid_name2 = 1 //revive:disable-line:var-naming I'm an Eiffel programmer thus I like underscores
|
||||
}
|
||||
|
||||
|
||||
func foo2() {
|
||||
// revive:disable-next-line:var-naming
|
||||
//revive:disable
|
||||
@ -22,3 +29,10 @@ func foo3() {
|
||||
/* revive:disable-next-line:var-naming */
|
||||
var invalid_name3 = 0 // MATCH /don't use underscores in Go names; var invalid_name3 should be invalidName3/
|
||||
}
|
||||
|
||||
func foo2p1() {
|
||||
//revive:disable Underscores are fine
|
||||
var invalid_name = 0
|
||||
//revive:enable No! Underscores are not nice!
|
||||
var invalid_name2 = 1 // MATCH /don't use underscores in Go names; var invalid_name2 should be invalidName2/
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ type issue struct {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Checkstyle) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var issues = map[string][]issue{}
|
||||
for failure := range failures {
|
||||
buf := new(bytes.Buffer)
|
||||
|
@ -18,7 +18,7 @@ func (f *Default) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Default) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (f *Friendly) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Friendly) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
errorMap := map[string]int{}
|
||||
warningMap := map[string]int{}
|
||||
totalErrors := 0
|
||||
|
@ -24,7 +24,7 @@ type jsonObject struct {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *JSON) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *JSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var slice []jsonObject
|
||||
for failure := range failures {
|
||||
obj := jsonObject{}
|
||||
|
@ -19,7 +19,7 @@ func (f *NDJSON) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *NDJSON) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
for failure := range failures {
|
||||
obj := jsonObject{}
|
||||
|
@ -18,7 +18,7 @@ func (f *Plain) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Plain) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName)
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package formatter
|
||||
|
||||
import "github.com/mgechev/revive/lint"
|
||||
|
||||
func severity(config lint.RulesConfig, failure lint.Failure) lint.Severity {
|
||||
if config, ok := config[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
func severity(config lint.Config, failure lint.Failure) lint.Severity {
|
||||
if config, ok := config.Rules[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
return lint.SeverityError
|
||||
}
|
||||
if config, ok := config.Directives[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
return lint.SeverityError
|
||||
}
|
||||
return lint.SeverityWarning
|
||||
|
@ -32,7 +32,7 @@ func formatFailure(failure lint.Failure, severity lint.Severity) []string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Stylish) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Stylish) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var result [][]string
|
||||
var totalErrors = 0
|
||||
var total = 0
|
||||
|
@ -19,7 +19,7 @@ func (f *Unix) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Unix) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Unix) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
||||
}
|
||||
|
@ -12,12 +12,21 @@ type RuleConfig struct {
|
||||
// RulesConfig defines the config for all rules.
|
||||
type RulesConfig = map[string]RuleConfig
|
||||
|
||||
// DirectiveConfig is type used for the linter directive configuration.
|
||||
type DirectiveConfig struct {
|
||||
Severity Severity
|
||||
}
|
||||
|
||||
// DirectivesConfig defines the config for all directives.
|
||||
type DirectivesConfig = map[string]DirectiveConfig
|
||||
|
||||
// Config defines the config of the linter.
|
||||
type Config struct {
|
||||
IgnoreGeneratedHeader bool `toml:"ignoreGeneratedHeader"`
|
||||
Confidence float64
|
||||
Severity Severity
|
||||
Rules RulesConfig `toml:"rule"`
|
||||
ErrorCode int `toml:"errorCode"`
|
||||
WarningCode int `toml:"warningCode"`
|
||||
Rules RulesConfig `toml:"rule"`
|
||||
ErrorCode int `toml:"errorCode"`
|
||||
WarningCode int `toml:"warningCode"`
|
||||
Directives DirectivesConfig `toml:"directive"`
|
||||
}
|
||||
|
41
lint/file.go
41
lint/file.go
@ -97,9 +97,12 @@ func (f *File) isMain() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const directiveSpecifyDisableReason = "specify-disable-reason"
|
||||
|
||||
func (f *File) lint(rules []Rule, config Config, failures chan Failure) {
|
||||
rulesConfig := config.Rules
|
||||
disabledIntervals := f.disabledIntervals(rules)
|
||||
_, mustSpecifyDisableReason := config.Directives[directiveSpecifyDisableReason]
|
||||
disabledIntervals := f.disabledIntervals(rules, mustSpecifyDisableReason, failures)
|
||||
for _, currentRule := range rules {
|
||||
ruleConfig := rulesConfig[currentRule.Name()]
|
||||
currentFailures := currentRule.Apply(f, ruleConfig.Arguments)
|
||||
@ -126,9 +129,15 @@ type enableDisableConfig struct {
|
||||
position int
|
||||
}
|
||||
|
||||
func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
re := regexp.MustCompile(`^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*$`)
|
||||
const directiveRE = `^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*(?: (.+))?$`
|
||||
const directivePos = 1
|
||||
const modifierPos = 2
|
||||
const rulesPos = 3
|
||||
const reasonPos = 4
|
||||
|
||||
var re = regexp.MustCompile(directiveRE)
|
||||
|
||||
func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, failures chan Failure) disabledIntervalsMap {
|
||||
enabledDisabledRulesMap := make(map[string][]enableDisableConfig)
|
||||
|
||||
getEnabledDisabledIntervals := func() disabledIntervalsMap {
|
||||
@ -202,16 +211,26 @@ func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
}
|
||||
|
||||
ruleNames := []string{}
|
||||
if len(match) > 2 {
|
||||
tempNames := strings.Split(match[3], ",")
|
||||
for _, name := range tempNames {
|
||||
name = strings.Trim(name, "\n")
|
||||
if len(name) > 0 {
|
||||
ruleNames = append(ruleNames, name)
|
||||
}
|
||||
tempNames := strings.Split(match[rulesPos], ",")
|
||||
for _, name := range tempNames {
|
||||
name = strings.Trim(name, "\n")
|
||||
if len(name) > 0 {
|
||||
ruleNames = append(ruleNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
mustCheckDisablingReason := mustSpecifyDisableReason && match[directivePos] == "disable"
|
||||
if mustCheckDisablingReason && strings.Trim(match[reasonPos], " ") == "" {
|
||||
failures <- Failure{
|
||||
Confidence: 1,
|
||||
RuleName: directiveSpecifyDisableReason,
|
||||
Failure: "reason of lint disabling not found",
|
||||
Position: ToFailurePosition(c.Pos(), c.End(), f),
|
||||
Node: c,
|
||||
}
|
||||
continue // skip this linter disabling directive
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
if len(ruleNames) == 0 {
|
||||
for _, rule := range rules {
|
||||
@ -219,7 +238,7 @@ func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
}
|
||||
}
|
||||
|
||||
handleRules(filename, match[2], match[1] == "enable", line, ruleNames)
|
||||
handleRules(filename, match[modifierPos], match[directivePos] == "enable", line, ruleNames)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,6 @@ type FormatterMetadata struct {
|
||||
|
||||
// Formatter defines an interface for failure formatters
|
||||
type Formatter interface {
|
||||
Format(<-chan Failure, RulesConfig) (string, error)
|
||||
Format(<-chan Failure, Config) (string, error)
|
||||
Name() string
|
||||
}
|
||||
|
6
main.go
6
main.go
@ -44,7 +44,7 @@ func main() {
|
||||
|
||||
var output string
|
||||
go (func() {
|
||||
output, err = formatter.Format(formatChan, config.Rules)
|
||||
output, err = formatter.Format(formatChan, *config)
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
}
|
||||
@ -62,6 +62,10 @@ func main() {
|
||||
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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user