Skip to content

Commit

Permalink
Conf reason rule disabling (#193)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
chavacava authored and mgechev committed Aug 2, 2019
1 parent 639585f commit 55cfae6
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 28 deletions.
26 changes: 24 additions & 2 deletions README.md
Expand Up @@ -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:

Expand All @@ -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:
Expand Down Expand Up @@ -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
}
```
Expand Down
6 changes: 6 additions & 0 deletions config.go
Expand Up @@ -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
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions fixtures/disable-annotations2.go
Expand Up @@ -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
Expand All @@ -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/
}
2 changes: 1 addition & 1 deletion formatter/checkstyle.go
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion formatter/default.go
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion formatter/friendly.go
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion formatter/json.go
Expand Up @@ -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{}
Expand Down
2 changes: 1 addition & 1 deletion formatter/ndjson.go
Expand Up @@ -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{}
Expand Down
2 changes: 1 addition & 1 deletion formatter/plain.go
Expand Up @@ -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)
}
Expand Down
7 changes: 5 additions & 2 deletions formatter/severity.go
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion formatter/stylish.go
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion formatter/unix.go
Expand Up @@ -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)
}
Expand Down
15 changes: 12 additions & 3 deletions lint/config.go
Expand Up @@ -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 changes: 30 additions & 11 deletions lint/file.go
Expand Up @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -202,14 +211,24 @@ 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
Expand All @@ -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)
}
}

Expand Down
2 changes: 1 addition & 1 deletion lint/formatter.go
Expand Up @@ -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 changes: 5 additions & 1 deletion main.go
Expand Up @@ -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())
}
Expand All @@ -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
}

Expand Down

0 comments on commit 55cfae6

Please sign in to comment.