Skip to content

Commit 15dafce

Browse files
authored
feat: add alphabetical option (#26)
1 parent 44caab8 commit 15dafce

19 files changed

+563
-42
lines changed

.github/workflows/pr-checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
- name: golangci-lint
4141
uses: golangci/golangci-lint-action@v7
4242
with:
43-
version: v2.1.0
43+
version: v2.1.1
4444

4545
spell:
4646
name: "Spell check"

README.md

Lines changed: 127 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
1+
# 🧐 FuncOrder
2+
13
[![Go Report Card](https://goreportcard.com/badge/github.com/manuelarte/funcorder)](https://goreportcard.com/report/github.com/manuelarte/funcorder)
24
![version](https://img.shields.io/github/v/release/manuelarte/funcorder)
35

46
- [🧐 FuncOrder](#-funcorder)
5-
- [⬇️ Getting Started](#-getting-started)
7+
- [⬇️ Getting Started](#️-getting-started)
8+
- [As A Golangci-lint linter](#as-a-golangci-lint-linter)
9+
- [Standalone application](#standalone-application)
610
- [🚀 Features](#-features)
711
- [Check exported methods are placed before non-exported methods](#check-exported-methods-are-placed-before-non-exported-methods)
812
- [Check `Constructors` functions are placed after struct declaration](#check-constructors-functions-are-placed-after-struct-declaration)
13+
- [Check Constructors/Methods are sorted alphabetically](#check-constructorsmethods-are-sorted-alphabetically)
914
- [Resources](#resources)
1015

11-
# 🧐 FuncOrder
12-
1316
Go Linter to check Functions/Methods Order.
1417

1518
## ⬇️ Getting Started
1619

20+
### As a golangci-lint linter
21+
22+
Define the rules in your `golangci-lint` configuration file, e.g:
23+
24+
```yaml
25+
linters:
26+
enable:
27+
- funcorder
28+
...
29+
30+
settings:
31+
funcorder:
32+
# Checks that constructors are placed after the structure declaration.
33+
# Default: true
34+
constructor: false
35+
# Checks if the exported methods of a structure are placed before the non-exported ones.
36+
# Default: true
37+
struct-method: false
38+
# Checks if the constructors and/or structure methods are sorted alphabetically.
39+
# Default: false
40+
alphabetical: true
41+
```
42+
43+
### Standalone application
44+
1745
Install FuncOrder linter using
1846
1947
```bash
@@ -23,13 +51,14 @@ go install github.com/manuelarte/funcorder@latest
2351
And then use it with
2452

2553
```
26-
funcorder [-constructor=true|false] [-struct-method=true|false] ./...
54+
funcorder [-constructor=true|false] [-struct-method=true|false] [-alphabetical=true|false] ./...
2755
```
2856

2957
Parameters:
3058

31-
- `constructor`: `true|false` (default `true`) Check that constructor is placed after struct declaration and before struct's methods.
32-
- `struct-method`: `true|false` (default `true`) Check that exported struct's methods are declared before non-exported.
59+
- `constructor`: `true|false` (default `true`) Checks that constructors are placed after the structure declaration.
60+
- `struct-method`: `true|false` (default `true`) Checks if the exported methods of a structure are placed before the non-exported ones.
61+
- `alphabetical`: `true|false` (default `false`) Checks if the constructors and/or structure methods are sorted alphabetically.
3362

3463
## 🚀 Features
3564

@@ -137,6 +166,98 @@ func NewMyStruct() MyStruct {
137166
</tbody>
138167
</table>
139168

169+
### Check Constructors/Methods are sorted alphabetically
170+
171+
This rule checks:
172+
173+
- `Constructor` functions are sorted alphabetically (if `constructor` setting/parameter is `true`).
174+
- `Methods` are sorted alphabetically (if `struct-method` setting/parameter is `true`) for each group (exported and non-exported).
175+
176+
<table>
177+
<thead><tr><th>❌ Bad</th><th>✅ Good</th></tr></thead>
178+
<tbody>
179+
<tr><td>
180+
181+
```go
182+
type MyStruct struct {
183+
Name string
184+
}
185+
186+
func NewMyStruct() MyStruct {
187+
return MyStruct{Name: "John"}
188+
}
189+
190+
// ❌ constructor "NewAMyStruct" should be placed
191+
// before "NewMyStruct"
192+
func NewAMyStruct() MyStruct {
193+
return MyStruct{Name: "John"}
194+
}
195+
196+
func (m MyStruct) GoodMorning() string {
197+
return "good morning"
198+
}
199+
200+
// ❌ method "GoodAfternoon" should be placed
201+
// before "GoodMorning"
202+
func (m MyStruct) GoodAfternoon() string {
203+
return "good afternoon"
204+
}
205+
206+
func (m MyStruct) hello() string {
207+
return "hello"
208+
}
209+
210+
// ❌ method "bye" should be placed
211+
// before "hello"
212+
func (m MyStruct) bye() string {
213+
return "bye"
214+
}
215+
216+
...
217+
```
218+
219+
</td><td>
220+
221+
```go
222+
type MyStruct struct {
223+
Name string
224+
}
225+
226+
// ✅ constructors sorted alphabetically
227+
func NewAMyStruct() MyStruct {
228+
return MyStruct{Name: "John"}
229+
}
230+
231+
func NewMyStruct() MyStruct {
232+
return MyStruct{Name: "John"}
233+
}
234+
235+
// ✅ exported methods sorted alphabetically
236+
func (m MyStruct) GoodAfternoon() string {
237+
return "good afternoon"
238+
}
239+
240+
func (m MyStruct) GoodMorning() string {
241+
return "good morning"
242+
}
243+
244+
// ✅ non-exported methods sorted alphabetically
245+
func (m MyStruct) bye() string {
246+
return "bye"
247+
}
248+
249+
func (m MyStruct) hello() string {
250+
return "hello"
251+
}
252+
253+
...
254+
```
255+
256+
</td></tr>
257+
258+
</tbody>
259+
</table>
260+
140261
## Resources
141262

142263
- Following Uber Style Guidelines about [function-grouping-and-ordering](https://github.com/uber-go/guide/blob/master/style.md#function-grouping-and-ordering)

analyzer/analyzer.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
const (
1515
ConstructorCheckName = "constructor"
1616
StructMethodCheckName = "struct-method"
17+
AlphabeticalCheckName = "alphabetical"
1718
)
1819

1920
func NewAnalyzer() *analysis.Analyzer {
@@ -27,27 +28,33 @@ func NewAnalyzer() *analysis.Analyzer {
2728
}
2829

2930
a.Flags.BoolVar(&f.constructorCheck, ConstructorCheckName, true,
30-
"Enable/disable feature to check constructors are placed after struct declaration")
31+
"Checks that constructors are placed after the structure declaration.")
3132
a.Flags.BoolVar(&f.structMethodCheck, StructMethodCheckName, true,
32-
"Enable/disable feature to check whether the exported struct's methods "+
33-
"are placed before the non-exported")
33+
"Checks if the exported methods of a structure are placed before the non-exported ones.")
34+
a.Flags.BoolVar(&f.alphabeticalCheck, AlphabeticalCheckName, false,
35+
"Checks if the constructors and/or structure methods are sorted alphabetically.")
3436

3537
return a
3638
}
3739

3840
type funcorder struct {
3941
constructorCheck bool
4042
structMethodCheck bool
43+
alphabeticalCheck bool
4144
}
4245

4346
func (f *funcorder) run(pass *analysis.Pass) (any, error) {
4447
var enabledCheckers features.Feature
4548
if f.constructorCheck {
46-
enabledCheckers |= features.ConstructorCheck
49+
enabledCheckers.Enable(features.ConstructorCheck)
4750
}
4851

4952
if f.structMethodCheck {
50-
enabledCheckers |= features.StructMethodCheck
53+
enabledCheckers.Enable(features.StructMethodCheck)
54+
}
55+
56+
if f.alphabeticalCheck {
57+
enabledCheckers.Enable(features.AlphabeticalCheck)
5158
}
5259

5360
fp := fileprocessor.NewFileProcessor(enabledCheckers)

analyzer/analyzer_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,16 @@ func TestAnalyzer(t *testing.T) {
1313
options map[string]string
1414
}{
1515
{
16-
desc: "all",
16+
desc: "default",
1717
patterns: "simple",
1818
},
19+
{
20+
desc: "all options",
21+
patterns: "simple-alphabetical",
22+
options: map[string]string{
23+
AlphabeticalCheckName: "true",
24+
},
25+
},
1926
{
2027
desc: "constructor check only",
2128
patterns: "constructor-check",
@@ -24,6 +31,15 @@ func TestAnalyzer(t *testing.T) {
2431
StructMethodCheckName: "false",
2532
},
2633
},
34+
{
35+
desc: "constructor check and alphabetical",
36+
patterns: "constructor-check-alphabetical",
37+
options: map[string]string{
38+
ConstructorCheckName: "true",
39+
StructMethodCheckName: "false",
40+
AlphabeticalCheckName: "true",
41+
},
42+
},
2743
{
2844
desc: "method check only",
2945
patterns: "struct-method-check",
@@ -32,6 +48,15 @@ func TestAnalyzer(t *testing.T) {
3248
StructMethodCheckName: "true",
3349
},
3450
},
51+
{
52+
desc: "alphabetical method check",
53+
patterns: "struct-method-check-alphabetical",
54+
options: map[string]string{
55+
ConstructorCheckName: "false",
56+
StructMethodCheckName: "true",
57+
AlphabeticalCheckName: "true",
58+
},
59+
},
3560
}
3661

3762
for _, test := range testCases {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package simple
2+
3+
type MyStruct2 struct {
4+
Name string
5+
}
6+
7+
func (m MyStruct2) GetName() string {
8+
return m.Name
9+
}
10+
11+
func (m *MyStruct2) SetName(name string) {
12+
m.Name = name
13+
}
14+
15+
func NewOtherMyStruct2() (m *MyStruct2) { // want `constructor "NewOtherMyStruct2" for struct "MyStruct2" should be placed before struct method "GetName"`
16+
m = &MyStruct2{Name: "John"}
17+
return
18+
}
19+
20+
func NewMyStruct2() *MyStruct2 { // want `constructor "NewMyStruct2" for struct "MyStruct2" should be placed before struct method "GetName"` `constructor \"NewMyStruct2\" for struct \"MyStruct2\" should be placed before constructor \"NewOtherMyStruct2\"`
21+
return &MyStruct2{Name: "John"}
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package simple
2+
3+
func NewOtherMyStruct() (m *MyStruct) { // want `should be placed after the struct declaration`
4+
m = &MyStruct{Name: "John"}
5+
return
6+
}
7+
8+
func NewMyStruct() *MyStruct { // want `should be placed after the struct declaration` `constructor \"NewMyStruct\" for struct \"MyStruct\" should be placed before constructor \"NewOtherMyStruct\"`
9+
return &MyStruct{Name: "John"}
10+
}
11+
12+
func MustMyStruct() *MyStruct { // want `should be placed after the struct declaration` `constructor \"MustMyStruct\" for struct \"MyStruct\" should be placed before constructor \"NewMyStruct\"`
13+
return NewMyStruct()
14+
}
15+
16+
type MyStruct struct {
17+
Name string
18+
}
19+
20+
func (m MyStruct) lenName() int {
21+
return len(m.Name)
22+
}
23+
24+
func (m MyStruct) GetName() string {
25+
return m.Name
26+
}
27+
28+
func (m *MyStruct) SetName(name string) {
29+
m.Name = name
30+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package simple
2+
3+
type Greetings struct {
4+
}
5+
6+
func NewOtherGreetings() (m *Greetings) {
7+
m = &Greetings{}
8+
return
9+
}
10+
11+
func NewGreetings() *Greetings { // want `constructor \"NewGreetings\" for struct \"Greetings\" should be placed before constructor \"NewOtherGreetings\"`
12+
return &Greetings{}
13+
}
14+
15+
func (m Greetings) GoodMorning() string {
16+
return "hello"
17+
}
18+
19+
func (m *Greetings) GoodAfternoon(name string) string {
20+
return "bye"
21+
}
22+
23+
func (m Greetings) hello() string {
24+
return "hello"
25+
}
26+
27+
func (m *Greetings) bye(name string) string {
28+
return "bye"
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package simple
2+
3+
import (
4+
"time"
5+
)
6+
7+
func NewOtherWayMyStruct() MyStruct {
8+
return MyStruct{Name: "John"}
9+
}
10+
11+
func NewTimeStruct() time.Time {
12+
return time.Now()
13+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package simple
2+
3+
type MyStruct2 struct {
4+
Name string
5+
}
6+
7+
func (m MyStruct2) GetName() string {
8+
return m.Name
9+
}
10+
11+
func (m *MyStruct2) SetName(name string) {
12+
m.Name = name
13+
}
14+
15+
func NewOtherMyStruct2() (m *MyStruct2) { // want `constructor \"NewOtherMyStruct2\" for struct \"MyStruct2\" should be placed before struct method \"GetName\"`
16+
m = &MyStruct2{Name: "John"}
17+
return
18+
}
19+
20+
func NewMyStruct2() *MyStruct2 { // want `constructor \"NewMyStruct2\" for struct \"MyStruct2\" should be placed before struct method \"GetName\"` `constructor \"NewMyStruct2\" for struct \"MyStruct2\" should be placed before constructor \"NewOtherMyStruct2\"`
21+
return &MyStruct2{Name: "John"}
22+
}

0 commit comments

Comments
 (0)