1
0
mirror of https://github.com/mgechev/revive.git synced 2025-11-25 22:12:38 +02:00

var-naming: detect meaningless package names (#1312)

This commit is contained in:
Oleksandr Redko
2025-04-16 12:30:28 +03:00
committed by GitHub
parent 57ed899d0c
commit 558580268e
6 changed files with 104 additions and 14 deletions

View File

@@ -1118,12 +1118,14 @@ _Configuration_: N/A
_Description_: This rule warns when [initialism](https://go.dev/wiki/CodeReviewComments#initialisms), [variable](https://go.dev/wiki/CodeReviewComments#variable-names) or [package](https://go.dev/wiki/CodeReviewComments#package-names) naming conventions are not followed. It ignores functions starting with `Example`, `Test`, `Benchmark`, and `Fuzz` in test files, preserving `golint` original behavior.
_Configuration_: This rule accepts two slices of strings and one optional slice with single map with named parameters.
(it's due to TOML hasn't "slice of any" and we keep backward compatibility with previous config version)
First slice is an allowlist and second one is a blocklist of initialisms.
In map, you can add boolean `upperCaseConst` (`uppercaseconst`, `upper-case-const`) parameter to allow `UPPER_CASE` for `const`
You can also add boolean `skipPackageNameChecks` (`skippackagenamechecks`, `skip-package-name-checks`) to skip package name checks.
By default, the rule behaves exactly as the alternative in `golint` but optionally, you can relax it (see [golint/lint/issues/89](https://github.com/golang/lint/issues/89))
_Configuration_: This rule accepts two slices of strings and one optional slice containing a single map with named parameters.
(This is because TOML does not support "slice of any," and we maintain backward compatibility with the previous configuration version).
The first slice is an allowlist, and the second one is a blocklist of initialisms.
In the map, you can add a boolean `upperCaseConst` (`uppercaseconst`, `upper-case-const`) parameter to allow `UPPER_CASE` for `const`.
You can also add a boolean `skipPackageNameChecks` (`skippackagenamechecks`, `skip-package-name-checks`) to skip package name checks.
When `skipPackageNameChecks` is false (the default), you can configure `extraBadPackageNames` (`extrabadpackagenames`, `extra-bad-package-names`) to forbid using the values from the list as package names additionally to the standard meaningless ones: "common", "interfaces", "misc", "types", "util", "utils".
By default, the rule behaves exactly as the alternative in `golint` but optionally, you can relax it (see [golint/lint/issues/89](https://github.com/golang/lint/issues/89)).
Examples:
@@ -1137,6 +1139,11 @@ Examples:
arguments = [[], [], [{skipPackageNameChecks=true}]]
```
```toml
[rule.var-naming]
arguments = [[], [], [{extraBadPackageNames=["helpers", "models"]}]]
```
```toml
[rule.var-naming]
arguments = [["ID"], ["VM"], [{upper-case-const=true}]]
@@ -1147,6 +1154,11 @@ Examples:
arguments = [[], [], [{skip-package-name-checks=true}]]
```
```toml
[rule.var-naming]
arguments = [[], [], [{extra-bad-package-names=["helpers", "models"]}]]
```
## waitgroup-by-value
_Description_: Function parameters that are passed by value, are in fact a copy of the original argument. Passing a copy of a `sync.WaitGroup` is usually not what the developer wants to do.

View File

@@ -22,12 +22,26 @@ var knownNameExceptions = map[string]bool{
"kWh": true,
}
// defaultBadPackageNames is the list of "bad" package names from https://go.dev/wiki/CodeReviewComments#package-names
// and https://go.dev/blog/package-names#bad-package-names.
// The rule warns about the usage of any package name in this list if skipPackageNameChecks is false.
// Values in the list should be lowercased.
var defaultBadPackageNames = map[string]struct{}{
"common": {},
"interfaces": {},
"misc": {},
"types": {},
"util": {},
"utils": {},
}
// VarNamingRule lints the name of a variable.
type VarNamingRule struct {
allowList []string
blockList []string
allowUpperCaseConst bool // if true - allows to use UPPER_SOME_NAMES for constants
skipPackageNameChecks bool
skipPackageNameChecks bool // check for meaningless and user-defined bad package names
extraBadPackageNames map[string]struct{} // inactive if skipPackageNameChecks is false
}
// Configure validates the rule configuration, and configures the rule accordingly.
@@ -70,27 +84,62 @@ func (r *VarNamingRule) Configure(arguments lint.Arguments) error {
r.allowUpperCaseConst = fmt.Sprint(v) == "true"
case isRuleOption(k, "skipPackageNameChecks"):
r.skipPackageNameChecks = fmt.Sprint(v) == "true"
case isRuleOption(k, "extraBadPackageNames"):
extraBadPackageNames, ok := v.([]string)
if !ok {
return fmt.Errorf("invalid third argument to the var-naming rule. Expecting extraBadPackageNames of type slice of strings, but %T", v)
}
for _, name := range extraBadPackageNames {
if r.extraBadPackageNames == nil {
r.extraBadPackageNames = map[string]struct{}{}
}
r.extraBadPackageNames[strings.ToLower(name)] = struct{}{}
}
}
}
}
return nil
}
func (*VarNamingRule) applyPackageCheckRules(walker *lintNames) {
func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
node := walker.fileAst.Name
packageName := node.Name
lowerPackageName := strings.ToLower(packageName)
if _, ok := r.extraBadPackageNames[lowerPackageName]; ok {
walker.onFailure(lint.Failure{
Failure: "avoid bad package names",
Confidence: 1,
Node: node,
Category: lint.FailureCategoryNaming,
})
return
}
if _, ok := defaultBadPackageNames[lowerPackageName]; ok {
walker.onFailure(lint.Failure{
Failure: "avoid meaningless package names",
Confidence: 1,
Node: node,
Category: lint.FailureCategoryNaming,
})
return
}
// Package names need slightly different handling than other names.
if strings.Contains(walker.fileAst.Name.Name, "_") && !strings.HasSuffix(walker.fileAst.Name.Name, "_test") {
if strings.Contains(packageName, "_") && !strings.HasSuffix(packageName, "_test") {
walker.onFailure(lint.Failure{
Failure: "don't use an underscore in package name",
Confidence: 1,
Node: walker.fileAst.Name,
Node: node,
Category: lint.FailureCategoryNaming,
})
}
if anyCapsRE.MatchString(walker.fileAst.Name.Name) {
if anyCapsRE.MatchString(packageName) {
walker.onFailure(lint.Failure{
Failure: fmt.Sprintf("don't use MixedCaps in package name; %s should be %s", walker.fileAst.Name.Name, strings.ToLower(walker.fileAst.Name.Name)),
Failure: fmt.Sprintf("don't use MixedCaps in package name; %s should be %s", packageName, lowerPackageName),
Confidence: 1,
Node: walker.fileAst.Name,
Node: node,
Category: lint.FailureCategoryNaming,
})
}

View File

@@ -2,6 +2,7 @@ package rule
import (
"errors"
"reflect"
"testing"
"github.com/mgechev/revive/lint"
@@ -16,6 +17,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
wantBlockList []string
wantAllowUpperCaseConst bool
wantSkipPackageNameChecks bool
wantBadPackageNames map[string]struct{}
}{
{
name: "no arguments",
@@ -34,6 +36,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
[]any{map[string]any{
"upperCaseConst": true,
"skipPackageNameChecks": true,
"extraBadPackageNames": []string{"helpers", "models"},
}},
},
wantErr: nil,
@@ -41,6 +44,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
wantBlockList: []string{"VM"},
wantAllowUpperCaseConst: true,
wantSkipPackageNameChecks: true,
wantBadPackageNames: map[string]struct{}{"helpers": {}, "models": {}},
},
{
name: "valid lowercased arguments",
@@ -50,6 +54,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
[]any{map[string]any{
"uppercaseconst": true,
"skippackagenamechecks": true,
"extrabadpackagenames": []string{"helpers", "models"},
}},
},
wantErr: nil,
@@ -57,6 +62,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
wantBlockList: []string{"VM"},
wantAllowUpperCaseConst: true,
wantSkipPackageNameChecks: true,
wantBadPackageNames: map[string]struct{}{"helpers": {}, "models": {}},
},
{
name: "valid kebab-cased arguments",
@@ -66,6 +72,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
[]any{map[string]any{
"upper-case-const": true,
"skip-package-name-checks": true,
"extra-bad-package-names": []string{"helpers", "models"},
}},
},
wantErr: nil,
@@ -73,6 +80,7 @@ func TestVarNamingRule_Configure(t *testing.T) {
wantBlockList: []string{"VM"},
wantAllowUpperCaseConst: true,
wantSkipPackageNameChecks: true,
wantBadPackageNames: map[string]struct{}{"helpers": {}, "models": {}},
},
{
name: "invalid allowlist type",
@@ -104,6 +112,11 @@ func TestVarNamingRule_Configure(t *testing.T) {
arguments: lint.Arguments{[]any{"ID"}, []any{"VM"}, []any{123}},
wantErr: errors.New("invalid third argument to the var-naming rule. Expecting a options of type slice, of len==1, with map, but int"),
},
{
name: "invalid third argument extraBadPackageNames",
arguments: lint.Arguments{[]any{""}, []any{""}, []any{map[string]any{"extraBadPackageNames": []int{1}}}},
wantErr: errors.New("invalid third argument to the var-naming rule. Expecting extraBadPackageNames of type slice of strings, but []int"),
},
}
for _, tt := range tests {
@@ -131,6 +144,9 @@ func TestVarNamingRule_Configure(t *testing.T) {
if rule.skipPackageNameChecks != tt.wantSkipPackageNameChecks {
t.Errorf("unexpected skipPackageNameChecks: got = %v, want %v", rule.skipPackageNameChecks, tt.wantSkipPackageNameChecks)
}
if !reflect.DeepEqual(rule.extraBadPackageNames, tt.wantBadPackageNames) {
t.Errorf("unexpected extraBadPackageNames: got = %v, want %v", rule.extraBadPackageNames, tt.wantBadPackageNames)
}
})
}
}

View File

@@ -29,4 +29,15 @@ func TestVarNaming(t *testing.T) {
testRule(t, "var_naming_skip_package_name_checks_true", &rule.VarNamingRule{}, &lint.RuleConfig{
Arguments: []any{[]any{}, []any{}, []any{map[string]any{"skip-package-name-checks": true}}},
})
testRule(t, "var_naming_meaningless_package_name", &rule.VarNamingRule{}, &lint.RuleConfig{})
testRule(t, "var_naming_meaningless_package_name", &rule.VarNamingRule{}, &lint.RuleConfig{
Arguments: []any{[]any{}, []any{},
[]any{map[string]any{"skip-package-name-checks": false}},
},
})
testRule(t, "var_naming_bad_package_name", &rule.VarNamingRule{}, &lint.RuleConfig{
Arguments: []any{[]any{}, []any{},
[]any{map[string]any{"skip-package-name-checks": false, "extra-bad-package-names": []string{"helpers"}}},
},
})
}

View File

@@ -0,0 +1 @@
package helpers // MATCH /avoid bad package names/

View File

@@ -0,0 +1 @@
package util // MATCH /avoid meaningless package names/