Skip to content

Commit

Permalink
feat: add support for import-alias-naming rule (#881)
Browse files Browse the repository at this point in the history
  • Loading branch information
denisvmedia authored Aug 28, 2023
1 parent 0357df7 commit f900b6c
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
| [`datarace`](./RULES_DESCRIPTIONS.md#datarace) | n/a | Spots potential dataraces | no | no |
| [`comment-spacings`](./RULES_DESCRIPTIONS.md#comment-spacings) | []string | Warns on malformed comments | no | no |
| [`redundant-import-alias`](./RULES_DESCRIPTIONS.md#redundant-import-alias) | n/a | Warns on import aliases matching the imported package name | no | no |
| [`import-alias-naming`](./RULES_DESCRIPTIONS.md#import-alias-naming) | string (defaults to ^[a-z][a-z0-9]{0,}$) | Conventions around the naming of import aliases. | no | no |


## Configurable rules
Expand Down
17 changes: 17 additions & 0 deletions RULES_DESCRIPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ List of all available rules.
- [indent-error-flow](#indent-error-flow)
- [imports-blacklist](#imports-blacklist)
- [import-shadowing](#import-shadowing)
- [import-alias-naming](#import-alias-naming)
- [line-length-limit](#line-length-limit)
- [max-public-structs](#max-public-structs)
- [modifies-parameter](#modifies-parameter)
Expand Down Expand Up @@ -489,6 +490,22 @@ name of an imported package. This rule spots identifiers that shadow an import.

_Configuration_: N/A

## import-alias-naming

_Description_: Aligns with Go's naming conventions, as outlined in the official
[blog post](https://go.dev/blog/package-names). It enforces clear and lowercase import alias names, echoing
the principles of good package naming. Users can follow these guidelines by default or define a custom regex rule.
Importantly, aliases with underscores ("_") are always allowed.

_Configuration_: (string) regular expression to match the aliases (default: ^[a-z][a-z0-9]{0,}$)

Example:

```toml
[rule.import-alias-naming]
arguments =["^[a-z][a-z0-9]{0,}$"]
```

## line-length-limit

_Description_: Warns in the presence of code lines longer than a configured maximum.
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ var allRules = append([]lint.Rule{
&rule.CommentSpacingsRule{},
&rule.IfReturnRule{},
&rule.RedundantImportAlias{},
&rule.ImportAliasNamingRule{},
}, defaultRules...)

var allFormatters = []lint.Formatter{
Expand Down
79 changes: 79 additions & 0 deletions rule/import-alias-naming.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package rule

import (
"fmt"
"regexp"
"sync"

"github.com/mgechev/revive/lint"
)

// ImportAliasNamingRule lints import alias naming.
type ImportAliasNamingRule struct {
configured bool
namingRuleRegexp *regexp.Regexp
sync.Mutex
}

const defaultNamingRule = "^[a-z][a-z0-9]{0,}$"

var defaultNamingRuleRegexp = regexp.MustCompile(defaultNamingRule)

func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) {
r.Lock()
defer r.Unlock()
if r.configured {
return
}

if len(arguments) < 1 {
r.namingRuleRegexp = defaultNamingRuleRegexp
return
}

namingRule, ok := arguments[0].(string) // Alt. non panicking version
if !ok {
panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string, got %T", arguments[0], arguments[0]))
}

var err error
r.namingRuleRegexp, err = regexp.Compile(namingRule)
if err != nil {
panic(fmt.Sprintf("Invalid argument to the import-alias-naming rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err))
}
}

// Apply applies the rule to given file.
func (r *ImportAliasNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
r.configure(arguments)

var failures []lint.Failure

for _, is := range file.AST.Imports {
path := is.Path
if path == nil {
continue
}

alias := is.Name
if alias == nil || alias.Name == "_" {
continue
}

if !r.namingRuleRegexp.MatchString(alias.Name) {
failures = append(failures, lint.Failure{
Confidence: 1,
Failure: fmt.Sprintf("import name (%s) must match the regular expression: %s", alias.Name, r.namingRuleRegexp.String()),
Node: alias,
Category: "imports",
})
}
}

return failures
}

// Name returns the rule name.
func (*ImportAliasNamingRule) Name() string {
return "import-alias-naming"
}
15 changes: 15 additions & 0 deletions test/import-alias-naming_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package test

import (
"testing"

"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/rule"
)

func TestImportAliasNaming(t *testing.T) {
testRule(t, "import-alias-naming", &rule.ImportAliasNamingRule{})
testRule(t, "import-alias-naming-custom-config", &rule.ImportAliasNamingRule{}, &lint.RuleConfig{
Arguments: []any{`^[a-z]+$`},
})
}
16 changes: 16 additions & 0 deletions testdata/import-alias-naming-custom-config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package fixtures

import (
_ "strings"
bar_foo "strings" // MATCH /import name (bar_foo) must match the regular expression: ^[a-z]+$/
fooBAR "strings" // MATCH /import name (fooBAR) must match the regular expression: ^[a-z]+$/
v1 "strings" // MATCH /import name (v1) must match the regular expression: ^[a-z]+$/
magical "magic/hat"
)

func somefunc() {
fooBAR.Clone("")
bar_foo.Clone("")
v1.Clone("")
magical.Clone("")
}
16 changes: 16 additions & 0 deletions testdata/import-alias-naming.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package fixtures

import (
_ "strings"
bar_foo "strings" // MATCH /import name (bar_foo) must match the regular expression: ^[a-z][a-z0-9]{0,}$/
fooBAR "strings" // MATCH /import name (fooBAR) must match the regular expression: ^[a-z][a-z0-9]{0,}$/
v1 "strings"
magical "magic/hat"
)

func somefunc() {
fooBAR.Clone("")
bar_foo.Clone("")
v1.Clone("")
magical.Clone("")
}

0 comments on commit f900b6c

Please sign in to comment.