Skip to content

Commit eb18252

Browse files
authored
adds filename-format rule (#1092)
1 parent 511e4e6 commit eb18252

File tree

8 files changed

+125
-0
lines changed

8 files changed

+125
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
548548
| [`max-control-nesting`](./RULES_DESCRIPTIONS.md#max-control-nesting) | int (defaults to 5) | Sets restriction for maximum nesting of control structures. | no | no |
549549
| [`comments-density`](./RULES_DESCRIPTIONS.md#comments-density) | int (defaults to 0) | Enforces a minimum comment / code relation | no | no |
550550
| [`file-length-limit`](./RULES_DESCRIPTIONS.md#file-length-limit) | map (optional)| Enforces a maximum number of lines per file | no | no |
551+
| [`filename-format`](./RULES_DESCRIPTIONS.md#filename-format) | regular expression (optional) | Enforces the formatting of filenames | no | no |
551552

552553
## Configurable rules
553554

RULES_DESCRIPTIONS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ List of all available rules.
3939
- [exported](#exported)
4040
- [file-header](#file-header)
4141
- [file-length-limit](#file-length-limit)
42+
- [filename-format](#filename-format)
4243
- [flag-parameter](#flag-parameter)
4344
- [function-length](#function-length)
4445
- [function-result-limit](#function-result-limit)
@@ -519,6 +520,18 @@ Example:
519520
arguments = [{max=100,skipComments=true,skipBlankLines=true}]
520521
```
521522

523+
## filename-format
524+
_Description_: enforces conventions on source file names. By default, the rule enforces filenames of the form `^[_A-Za-z0-9][_A-Za-z0-9-]*.go$`: Optionally, the rule can be configured to enforce other forms.
525+
526+
_Configuration_: (string) regular expression for source filenames.
527+
528+
Example:
529+
530+
```toml
531+
[rule.filename-format]
532+
arguments=["^[_a-z][_a-z0-9]*.go$"]
533+
```
534+
522535
## flag-parameter
523536

524537
_Description_: If a function controls the flow of another by passing it information on what to do, both functions are said to be [control-coupled](https://en.wikipedia.org/wiki/Coupling_(computer_programming)#Procedural_programming).

config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ var allRules = append([]lint.Rule{
9797
&rule.MaxControlNestingRule{},
9898
&rule.CommentsDensityRule{},
9999
&rule.FileLengthLimitRule{},
100+
&rule.FilenameFormatRule{},
100101
}, defaultRules...)
101102

102103
var allFormatters = []lint.Formatter{

rule/filename-format.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package rule
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"regexp"
7+
"sync"
8+
"unicode"
9+
10+
"github.com/mgechev/revive/lint"
11+
)
12+
13+
// FilenameFormatRule lints source filenames according to a set of regular expressions given as arguments
14+
type FilenameFormatRule struct {
15+
format *regexp.Regexp
16+
sync.Mutex
17+
}
18+
19+
// Apply applies the rule to the given file.
20+
func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
21+
r.configure(arguments)
22+
23+
filename := filepath.Base(file.Name)
24+
if r.format.MatchString(filename) {
25+
return nil
26+
}
27+
28+
failureMsg := fmt.Sprintf("Filename %s is not of the format %s.%s", filename, r.format.String(), r.getMsgForNonAsciiChars(filename))
29+
return []lint.Failure{{
30+
Confidence: 1,
31+
Failure: failureMsg,
32+
RuleName: r.Name(),
33+
Node: file.AST.Name,
34+
}}
35+
}
36+
37+
func (r *FilenameFormatRule) getMsgForNonAsciiChars(str string) string {
38+
result := ""
39+
for _, c := range str {
40+
if c <= unicode.MaxASCII {
41+
continue
42+
}
43+
44+
result += fmt.Sprintf(" Non ASCII character %c (%U) found.", c, c)
45+
}
46+
47+
return result
48+
}
49+
50+
// Name returns the rule name.
51+
func (*FilenameFormatRule) Name() string {
52+
return "filename-format"
53+
}
54+
55+
var defaultFormat = regexp.MustCompile("^[_A-Za-z0-9][_A-Za-z0-9-]*.go$")
56+
57+
func (r *FilenameFormatRule) configure(arguments lint.Arguments) {
58+
r.Lock()
59+
defer r.Unlock()
60+
61+
if r.format != nil {
62+
return
63+
}
64+
65+
argsCount := len(arguments)
66+
if argsCount == 0 {
67+
r.format = defaultFormat
68+
return
69+
}
70+
71+
if argsCount > 1 {
72+
panic(fmt.Sprintf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments))
73+
}
74+
75+
arg := arguments[0]
76+
str, ok := arg.(string)
77+
if !ok {
78+
panic(fmt.Sprintf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg))
79+
}
80+
81+
format, err := regexp.Compile(str)
82+
if err != nil {
83+
panic(fmt.Sprintf("rule %q expects a valid regexp argument, got %v for %s", r.Name(), err, arg))
84+
}
85+
86+
r.format = format
87+
}

test/filename-format_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/mgechev/revive/lint"
7+
"github.com/mgechev/revive/rule"
8+
)
9+
10+
func TestLintFilenameFormat(t *testing.T) {
11+
testRule(t, "filename-ok-default", &rule.FilenameFormatRule{}, &lint.RuleConfig{})
12+
13+
testRule(t, "filenamе-with-non-ascii-char", &rule.FilenameFormatRule{}, &lint.RuleConfig{})
14+
15+
testRule(t, "filename_with_underscores", &rule.FilenameFormatRule{}, &lint.RuleConfig{Arguments: []any{"^[A-Za-z][A-Za-z0-9]*.go$"}})
16+
}

testdata/filename-ok-default.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package main

testdata/filename_with_underscores.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
// MATCH:1 /Filename filename_with_underscores.go is not of the format ^[A-Za-z][A-Za-z0-9]*.go$./
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
// MATCH:1 /Filename filenamе-with-non-ascii-char.go is not of the format ^[_A-Za-z0-9][_A-Za-z0-9-]*.go$. Non ASCII character е (U+0435) found./

0 commit comments

Comments
 (0)