1
0
mirror of https://github.com/mgechev/revive.git synced 2025-06-02 22:57:22 +02:00

wip: new rule enforce-switch-default

This commit is contained in:
chavacava 2025-05-28 15:30:00 +02:00
parent 6becd540e4
commit b22e68f34a
6 changed files with 180 additions and 0 deletions

View File

@ -104,6 +104,7 @@ var allRules = append([]lint.Rule{
&rule.RedundantTestMainExitRule{},
&rule.UnnecessaryFormatRule{},
&rule.UseFmtPrintRule{},
&rule.EnforceSwitchDefaultRule{},
}, defaultRules...)
// allFormatters is a list of all available formatters to output the linting results.

View File

@ -0,0 +1,107 @@
package rule
import (
"fmt"
"go/ast"
"strings"
"github.com/mgechev/revive/lint"
)
// EnforceSwitchDefaultRule implements a rule to enforce default clauses use and/or position.
type EnforceSwitchDefaultRule struct {
allowNoDefault bool // allow absence of default
allowDefaultNotLast bool // allow default, if present, not being the last case
}
// Configure validates the rule configuration, and configures the rule accordingly.
//
// Configuration implements the [lint.ConfigurableRule] interface.
func (r *EnforceSwitchDefaultRule) Configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
return nil
}
for _, arg := range arguments {
argStr, ok := arg.(string)
if !ok {
return fmt.Errorf("invalid argument for rule %s; expected string but got %T", r.Name(), arg)
}
switch strings.ToLower(argStr) {
case "allownodefault":
r.allowNoDefault = true
case "allowdefaultnotlast":
r.allowDefaultNotLast = true
default:
return fmt.Errorf(`invalid argument %q for rule %s; expected "allowNoDefault" or "allowDefaultNotLast"`, argStr, r.Name())
}
}
return nil
}
// Apply applies the rule to given file.
func (r *EnforceSwitchDefaultRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
var failures []lint.Failure
astFile := file.AST
ast.Inspect(astFile, func(n ast.Node) bool {
switchNode, ok := n.(*ast.SwitchStmt)
if !ok {
return true // not a switch statement
}
defaultClause, isLast := r.seekDefaultCase(switchNode.Body)
hasDefault := defaultClause != nil
if !hasDefault && r.allowNoDefault {
return true // switch without default but the rule is configured to don´t care
}
if !hasDefault && !r.allowNoDefault {
// switch without default
failures = append(failures, lint.Failure{
Confidence: 1,
Node: switchNode,
Category: lint.FailureCategoryStyle,
Failure: "switch must have a default case clause",
})
return true
}
// the switch has a default
if r.allowDefaultNotLast || isLast {
return true
}
failures = append(failures, lint.Failure{
Confidence: 1,
Node: defaultClause,
Category: lint.FailureCategoryStyle,
Failure: "default case clause must be the last one",
})
return true
})
return failures
}
func (r *EnforceSwitchDefaultRule) seekDefaultCase(body *ast.BlockStmt) (defaultClause *ast.CaseClause, isLast bool) {
var last *ast.CaseClause
for _, stmt := range body.List {
cc, _ := stmt.(*ast.CaseClause) // no need to check for ok
last = cc
if cc.List == nil { // a nil List means "default"
defaultClause = cc
}
}
return defaultClause, defaultClause == last
}
// Name returns the rule name.
func (*EnforceSwitchDefaultRule) Name() string {
return "enforce-switch-default"
}

View File

@ -0,0 +1,18 @@
package test
import (
"testing"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)
func TestEnforceSwitchDefault(t *testing.T) {
testRule(t, "enforce_switch_default", &rule.EnforceSwitchDefaultRule{})
testRule(t, "enforce_switch_default_allow_no_default", &rule.EnforceSwitchDefaultRule{}, &lint.RuleConfig{
Arguments: []any{"allowNoDefault"},
})
testRule(t, "enforce_switch_default_allow_not_last", &rule.EnforceSwitchDefaultRule{}, &lint.RuleConfig{
Arguments: []any{"allowDefaultNotLast"},
})
}

18
testdata/enforce_switch_default.go vendored Normal file
View File

@ -0,0 +1,18 @@
package fixtures
func enforceSwitchDefault() {
switch expression {
case condition:
default:
}
switch expression {
default: // MATCH /default case clause must be the last one/
case condition:
}
switch expression { // MATCH /switch must have a default case clause/
case condition:
}
}

View File

@ -0,0 +1,18 @@
package fixtures
func enforceSwitchDefault() {
switch expression {
case condition:
default:
}
switch expression {
default: // MATCH /default case clause must be the last one/
case condition:
}
switch expression {
case condition:
}
}

View File

@ -0,0 +1,18 @@
package fixtures
func enforceSwitchDefault() {
switch expression {
case condition:
default:
}
switch expression {
default:
case condition:
}
switch expression { // MATCH /switch must have a default case clause/
case condition:
}
}