Skip to content

Commit 946abaa

Browse files
manuelartealexandearmanuelarte
authored
feat(fix): add support for --fix (#29)
* feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): draft supporting fix * Apply suggestions from code review Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * fixing compilation issues * small refactor * feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): draft supporting fix * feat(fix): supporting fix * adding directive test case * adding directive test case * using only one suggestedFix entry * applying alexandear's comments * fix: bug with first struct method with comments * refactor: small refactor --------- Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> Co-authored-by: manuelarte <manuel.doncel.martos@gmail.com>
1 parent 15dafce commit 946abaa

File tree

46 files changed

+1316
-176
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1316
-176
lines changed

README.md

+11-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- [As A Golangci-lint linter](#as-a-golangci-lint-linter)
99
- [Standalone application](#standalone-application)
1010
- [🚀 Features](#-features)
11-
- [Check exported methods are placed before non-exported methods](#check-exported-methods-are-placed-before-non-exported-methods)
11+
- [Check exported methods are placed before unexported methods](#check-exported-methods-are-placed-before-unexported-methods)
1212
- [Check `Constructors` functions are placed after struct declaration](#check-constructors-functions-are-placed-after-struct-declaration)
1313
- [Check Constructors/Methods are sorted alphabetically](#check-constructorsmethods-are-sorted-alphabetically)
1414
- [Resources](#resources)
@@ -32,7 +32,7 @@ linters:
3232
# Checks that constructors are placed after the structure declaration.
3333
# Default: true
3434
constructor: false
35-
# Checks if the exported methods of a structure are placed before the non-exported ones.
35+
# Checks if the exported methods of a structure are placed before the unexported ones.
3636
# Default: true
3737
struct-method: false
3838
# Checks if the constructors and/or structure methods are sorted alphabetically.
@@ -51,20 +51,21 @@ go install github.com/manuelarte/funcorder@latest
5151
And then use it with
5252

5353
```
54-
funcorder [-constructor=true|false] [-struct-method=true|false] [-alphabetical=true|false] ./...
54+
funcorder [-constructor=true|false] [-struct-method=true|false] [-alphabetical=true|false] [--fix] ./...
5555
```
5656

5757
Parameters:
5858

5959
- `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.
60+
- `struct-method`: `true|false` (default `true`) Checks if the exported methods of a structure are placed before the unexported ones.
6161
- `alphabetical`: `true|false` (default `false`) Checks if the constructors and/or structure methods are sorted alphabetically.
62+
- `fix`: Fix the source code to adhere to the enabled features.
6263

6364
## 🚀 Features
6465

65-
### Check exported methods are placed before non-exported methods
66+
### Check exported methods are placed before unexported methods
6667

67-
This rule checks that the exported method are placed before the non-exported ones, e.g:
68+
This rule checks that the exported method are placed before the unexported ones, e.g:
6869

6970
<table>
7071
<thead><tr><th>❌ Bad</th><th>✅ Good</th></tr></thead>
@@ -76,7 +77,7 @@ type MyStruct struct {
7677
Name string
7778
}
7879

79-
//non-exported method
80+
//unexported method
8081
// placed before exported method
8182
func (m MyStruct) lenName() int {
8283
return len(m.Name)
@@ -96,7 +97,7 @@ type MyStruct struct {
9697
}
9798

9899
// ✅ exported methods before
99-
// non-exported methods
100+
// unexported methods
100101
func (m MyStruct) GetName() string {
101102
return m.Name
102103
}
@@ -171,7 +172,7 @@ func NewMyStruct() MyStruct {
171172
This rule checks:
172173

173174
- `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+
- `Methods` are sorted alphabetically (if `struct-method` setting/parameter is `true`) for each group (exported and unexported).
175176

176177
<table>
177178
<thead><tr><th>❌ Bad</th><th>✅ Good</th></tr></thead>
@@ -241,7 +242,7 @@ func (m MyStruct) GoodMorning() string {
241242
return "good morning"
242243
}
243244

244-
//non-exported methods sorted alphabetically
245+
//unexported methods sorted alphabetically
245246
func (m MyStruct) bye() string {
246247
return "bye"
247248
}

analyzer/analyzer.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ func NewAnalyzer() *analysis.Analyzer {
2323
a := &analysis.Analyzer{
2424
Name: "funcorder",
2525
Doc: "checks the order of functions, methods, and constructors",
26+
URL: "https://github.com/manuelarte/funcorder",
2627
Run: f.run,
2728
Requires: []*analysis.Analyzer{inspect.Analyzer},
2829
}
2930

3031
a.Flags.BoolVar(&f.constructorCheck, ConstructorCheckName, true,
3132
"Checks that constructors are placed after the structure declaration.")
3233
a.Flags.BoolVar(&f.structMethodCheck, StructMethodCheckName, true,
33-
"Checks if the exported methods of a structure are placed before the non-exported ones.")
34+
"Checks if the exported methods of a structure are placed before the unexported ones.")
3435
a.Flags.BoolVar(&f.alphabeticalCheck, AlphabeticalCheckName, false,
3536
"Checks if the constructors and/or structure methods are sorted alphabetically.")
3637

@@ -57,7 +58,7 @@ func (f *funcorder) run(pass *analysis.Pass) (any, error) {
5758
enabledCheckers.Enable(features.AlphabeticalCheck)
5859
}
5960

60-
fp := fileprocessor.NewFileProcessor(enabledCheckers)
61+
fp := fileprocessor.NewFileProcessor(pass.Fset, enabledCheckers)
6162

6263
insp, found := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
6364
if !found {
@@ -71,10 +72,19 @@ func (f *funcorder) run(pass *analysis.Pass) (any, error) {
7172
(*ast.TypeSpec)(nil),
7273
}
7374

75+
var errProcessing error
7476
insp.Preorder(nodeFilter, func(n ast.Node) {
77+
if errProcessing != nil {
78+
return
79+
}
7580
switch node := n.(type) {
7681
case *ast.File:
77-
for _, report := range fp.Analyze() {
82+
reports, err := fp.Analyze()
83+
if err != nil {
84+
errProcessing = err
85+
return
86+
}
87+
for _, report := range reports {
7888
pass.Report(report)
7989
}
8090

@@ -87,8 +97,15 @@ func (f *funcorder) run(pass *analysis.Pass) (any, error) {
8797
fp.NewTypeSpec(node)
8898
}
8999
})
100+
if errProcessing != nil {
101+
return nil, errProcessing
102+
}
90103

91-
for _, report := range fp.Analyze() {
104+
reports, err := fp.Analyze()
105+
if err != nil {
106+
return nil, err
107+
}
108+
for _, report := range reports {
92109
pass.Report(report)
93110
}
94111

analyzer/analyzer_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,89 @@ func TestAnalyzer(t *testing.T) {
7373
})
7474
}
7575
}
76+
77+
func TestAnalyzerWithFix(t *testing.T) {
78+
testCases := []struct {
79+
desc string
80+
patterns string
81+
options map[string]string
82+
}{
83+
{
84+
desc: "constructor fix",
85+
patterns: "constructor-check-fix",
86+
options: map[string]string{
87+
ConstructorCheckName: "true",
88+
StructMethodCheckName: "false",
89+
AlphabeticalCheckName: "false",
90+
},
91+
},
92+
{
93+
desc: "constructor alphabetical fix",
94+
patterns: "constructor-check-alphabetical-fix",
95+
options: map[string]string{
96+
ConstructorCheckName: "true",
97+
StructMethodCheckName: "false",
98+
AlphabeticalCheckName: "true",
99+
},
100+
},
101+
{
102+
desc: "struct method fix",
103+
patterns: "struct-method-fix",
104+
options: map[string]string{
105+
ConstructorCheckName: "false",
106+
StructMethodCheckName: "true",
107+
AlphabeticalCheckName: "false",
108+
},
109+
},
110+
{
111+
desc: "struct method alphabetical fix",
112+
patterns: "struct-method-alphabetical-fix",
113+
options: map[string]string{
114+
ConstructorCheckName: "false",
115+
StructMethodCheckName: "true",
116+
AlphabeticalCheckName: "true",
117+
},
118+
},
119+
{
120+
desc: "simple fix",
121+
patterns: "simple-fix",
122+
options: map[string]string{
123+
ConstructorCheckName: "true",
124+
StructMethodCheckName: "true",
125+
AlphabeticalCheckName: "false",
126+
},
127+
},
128+
{
129+
desc: "simple alphabetical fix",
130+
patterns: "simple-alphabetical-fix",
131+
options: map[string]string{
132+
ConstructorCheckName: "true",
133+
StructMethodCheckName: "true",
134+
AlphabeticalCheckName: "true",
135+
},
136+
},
137+
{
138+
desc: "special cases",
139+
patterns: "special-cases-fix",
140+
options: map[string]string{
141+
ConstructorCheckName: "true",
142+
StructMethodCheckName: "true",
143+
AlphabeticalCheckName: "false",
144+
},
145+
},
146+
}
147+
148+
for _, test := range testCases {
149+
t.Run(test.desc, func(t *testing.T) {
150+
a := NewAnalyzer()
151+
152+
for k, v := range test.options {
153+
if err := a.Flags.Set(k, v); err != nil {
154+
t.Fatal(err)
155+
}
156+
}
157+
158+
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), a, test.patterns)
159+
})
160+
}
161+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package constructor_check_alphabetical_fix
2+
3+
type ConstructorAfterStructMethodsWithComments struct {
4+
Name string
5+
}
6+
7+
// GetName returns the name.
8+
func (m ConstructorAfterStructMethodsWithComments) GetName() string {
9+
return m.Name
10+
}
11+
12+
// SetName sets the name
13+
// multi line comment
14+
func (m *ConstructorAfterStructMethodsWithComments) SetName(name string) {
15+
m.Name = name
16+
}
17+
18+
// NewOtherConstructorAfterStructMethodsWithComments This constructor creates the
19+
// struct ConstructorAfterStructMethodsWithComments
20+
// with a named return
21+
func NewOtherConstructorAfterStructMethodsWithComments() (m *ConstructorAfterStructMethodsWithComments) { // want `constructor \"NewOtherConstructorAfterStructMethodsWithComments\" for struct \"ConstructorAfterStructMethodsWithComments\" should be placed before struct method \"GetName\"`
22+
m = &ConstructorAfterStructMethodsWithComments{Name: "John"}
23+
return
24+
}
25+
26+
func NewConstructorAfterStructMethodsWithComments() *ConstructorAfterStructMethodsWithComments { // want `constructor \"NewConstructorAfterStructMethodsWithComments\" for struct \"ConstructorAfterStructMethodsWithComments\" should be placed before struct method \"GetName\"` `constructor \"NewConstructorAfterStructMethodsWithComments\" for struct \"ConstructorAfterStructMethodsWithComments\" should be placed before constructor \"NewOtherConstructorAfterStructMethodsWithComments\"`
27+
return &ConstructorAfterStructMethodsWithComments{Name: "John"}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package constructor_check_alphabetical_fix
2+
3+
type ConstructorAfterStructMethodsWithComments struct {
4+
Name string
5+
}
6+
7+
func NewConstructorAfterStructMethodsWithComments() *ConstructorAfterStructMethodsWithComments {
8+
return &ConstructorAfterStructMethodsWithComments{Name: "John"}
9+
}
10+
11+
// NewOtherConstructorAfterStructMethodsWithComments This constructor creates the
12+
// struct ConstructorAfterStructMethodsWithComments
13+
// with a named return
14+
func NewOtherConstructorAfterStructMethodsWithComments() (m *ConstructorAfterStructMethodsWithComments) {
15+
m = &ConstructorAfterStructMethodsWithComments{Name: "John"}
16+
return
17+
}
18+
19+
// GetName returns the name.
20+
func (m ConstructorAfterStructMethodsWithComments) GetName() string {
21+
return m.Name
22+
}
23+
24+
// SetName sets the name
25+
// multi line comment
26+
func (m *ConstructorAfterStructMethodsWithComments) SetName(name string) {
27+
m.Name = name
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package constructor_check_alphabetical_fix
2+
3+
// NewOtherConstructorAfterStructMethodsWithComments This constructor creates the
4+
// struct ConstructorBeforeStructWithComments
5+
// with a named return
6+
func NewOtherConstructorBeforeStructWithComments() (m *ConstructorBeforeStructWithComments) { // want `constructor \"NewOtherConstructorBeforeStructWithComments\" for struct \"ConstructorBeforeStructWithComments\" should be placed after the struct declaration`
7+
m = &ConstructorBeforeStructWithComments{Name: "John"}
8+
return
9+
}
10+
11+
func NewConstructorBeforeStructWithComments() *ConstructorBeforeStructWithComments { // want `constructor \"NewConstructorBeforeStructWithComments\" for struct \"ConstructorBeforeStructWithComments\" should be placed after the struct declaration` `constructor \"NewConstructorBeforeStructWithComments\" for struct \"ConstructorBeforeStructWithComments\" should be placed before constructor \"NewOtherConstructorBeforeStructWithComments\"`
12+
return &ConstructorBeforeStructWithComments{Name: "John"}
13+
}
14+
15+
type ConstructorBeforeStructWithComments struct {
16+
Name string
17+
}
18+
19+
// GetName returns the name.
20+
func (m ConstructorBeforeStructWithComments) GetName() string {
21+
return m.Name
22+
}
23+
24+
// SetName sets the name
25+
// multi line comment
26+
func (m *ConstructorBeforeStructWithComments) SetName(name string) {
27+
m.Name = name
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package constructor_check_alphabetical_fix
2+
3+
type ConstructorBeforeStructWithComments struct {
4+
Name string
5+
}
6+
7+
func NewConstructorBeforeStructWithComments() *ConstructorBeforeStructWithComments {
8+
return &ConstructorBeforeStructWithComments{Name: "John"}
9+
}
10+
11+
// NewOtherConstructorAfterStructMethodsWithComments This constructor creates the
12+
// struct ConstructorBeforeStructWithComments
13+
// with a named return
14+
func NewOtherConstructorBeforeStructWithComments() (m *ConstructorBeforeStructWithComments) {
15+
m = &ConstructorBeforeStructWithComments{Name: "John"}
16+
return
17+
}
18+
19+
// GetName returns the name.
20+
func (m ConstructorBeforeStructWithComments) GetName() string {
21+
return m.Name
22+
}
23+
24+
// SetName sets the name
25+
// multi line comment
26+
func (m *ConstructorBeforeStructWithComments) SetName(name string) {
27+
m.Name = name
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package constructor_check_fix
2+
3+
type ConstructorAfterStructMethodsWithComments struct {
4+
Name string
5+
}
6+
7+
// GetName returns the name.
8+
func (m ConstructorAfterStructMethodsWithComments) GetName() string {
9+
return m.Name
10+
}
11+
12+
// SetName sets the name
13+
// multi line comment
14+
func (m *ConstructorAfterStructMethodsWithComments) SetName(name string) {
15+
m.Name = name
16+
}
17+
18+
// NewOtherConstructorAfterStructMethodsWithComments This constructor creates the
19+
// struct ConstructorAfterStructMethodsWithComments
20+
// with a named return
21+
func NewOtherConstructorAfterStructMethodsWithComments() (m *ConstructorAfterStructMethodsWithComments) { // want `constructor \"NewOtherConstructorAfterStructMethodsWithComments\" for struct \"ConstructorAfterStructMethodsWithComments\" should be placed before struct method \"GetName\"`
22+
m = &ConstructorAfterStructMethodsWithComments{Name: "John"}
23+
return
24+
}
25+
26+
func NewConstructorAfterStructMethodsWithComments() *ConstructorAfterStructMethodsWithComments { // want `constructor \"NewConstructorAfterStructMethodsWithComments\" for struct \"ConstructorAfterStructMethodsWithComments\" should be placed before struct method \"GetName\"`
27+
return &ConstructorAfterStructMethodsWithComments{Name: "John"}
28+
}

0 commit comments

Comments
 (0)