1
1
package lint
2
2
3
3
import (
4
+ "errors"
4
5
"go/ast"
5
6
"go/importer"
6
7
"go/token"
@@ -9,6 +10,7 @@ import (
9
10
10
11
goversion "github.com/hashicorp/go-version"
11
12
13
+ "github.com/mgechev/revive/internal/astutils"
12
14
"github.com/mgechev/revive/internal/typeparams"
13
15
)
14
16
@@ -31,7 +33,6 @@ type Package struct {
31
33
var (
32
34
trueValue = 1
33
35
falseValue = 2
34
- notSet = 3
35
36
36
37
go121 = goversion .Must (goversion .NewVersion ("1.21" ))
37
38
go122 = goversion .Must (goversion .NewVersion ("1.22" ))
@@ -111,6 +112,11 @@ func (p *Package) TypeCheck() error {
111
112
astFiles = append (astFiles , f .AST )
112
113
}
113
114
115
+ if anyFile == nil {
116
+ // this is unlikely to happen, but technically guarantees anyFile to not be nil
117
+ return errors .New ("no ast.File found" )
118
+ }
119
+
114
120
typesPkg , err := check (config , anyFile .AST .Name .Name , p .fset , astFiles , info )
115
121
116
122
// Remember the typechecking info, even if config.Check failed,
@@ -135,47 +141,40 @@ func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.
135
141
return config .Check (n , fset , astFiles , info )
136
142
}
137
143
138
- // TypeOf returns the type of an expression.
144
+ // TypeOf returns the type of expression.
139
145
func (p * Package ) TypeOf (expr ast.Expr ) types.Type {
140
146
if p .typesInfo == nil {
141
147
return nil
142
148
}
143
149
return p .typesInfo .TypeOf (expr )
144
150
}
145
151
146
- type walker struct {
147
- nmap map [string ]int
148
- has map [string ]int
149
- }
152
+ type sortableMethodsFlags int
150
153
151
- func (w * walker ) Visit (n ast.Node ) ast.Visitor {
152
- fn , ok := n .(* ast.FuncDecl )
153
- if ! ok || fn .Recv == nil || len (fn .Recv .List ) == 0 {
154
- return w
155
- }
156
- // TODO(dsymonds): We could check the signature to be more precise.
157
- recv := typeparams .ReceiverType (fn )
158
- if i , ok := w .nmap [fn .Name .Name ]; ok {
159
- w .has [recv ] |= i
160
- }
161
- return w
162
- }
154
+ // flags for sortable interface methods.
155
+ const (
156
+ bfLen sortableMethodsFlags = 1 << iota
157
+ bfLess
158
+ bfSwap
159
+ )
163
160
164
161
func (p * Package ) scanSortable () {
165
- p .sortable = map [string ]bool {}
166
-
167
- // bitfield for which methods exist on each type.
168
- const (
169
- bfLen = 1 << iota
170
- bfLess
171
- bfSwap
172
- )
173
- nmap := map [string ]int {"Len" : bfLen , "Less" : bfLess , "Swap" : bfSwap }
174
- has := map [string ]int {}
162
+ sortableFlags := map [string ]sortableMethodsFlags {}
175
163
for _ , f := range p .files {
176
- ast .Walk (& walker {nmap , has }, f .AST )
164
+ for _ , decl := range f .AST .Decls {
165
+ fn , ok := decl .(* ast.FuncDecl )
166
+ isAMethodDeclaration := ok && fn .Recv != nil && len (fn .Recv .List ) != 0
167
+ if ! isAMethodDeclaration {
168
+ continue
169
+ }
170
+
171
+ recvType := typeparams .ReceiverType (fn )
172
+ sortableFlags [recvType ] |= getSortableMethodFlagForFunction (fn )
173
+ }
177
174
}
178
- for typ , ms := range has {
175
+
176
+ p .sortable = make (map [string ]bool , len (sortableFlags ))
177
+ for typ , ms := range sortableFlags {
179
178
if ms == bfLen | bfLess | bfSwap {
180
179
p .sortable [typ ] = true
181
180
}
@@ -204,3 +203,16 @@ func (p *Package) IsAtLeastGo121() bool {
204
203
func (p * Package ) IsAtLeastGo122 () bool {
205
204
return p .goVersion .GreaterThanOrEqual (go122 )
206
205
}
206
+
207
+ func getSortableMethodFlagForFunction (fn * ast.FuncDecl ) sortableMethodsFlags {
208
+ switch {
209
+ case astutils .FuncSignatureIs (fn , "Len" , []string {}, []string {"int" }):
210
+ return bfLen
211
+ case astutils .FuncSignatureIs (fn , "Less" , []string {"int" , "int" }, []string {"bool" }):
212
+ return bfLess
213
+ case astutils .FuncSignatureIs (fn , "Swap" , []string {"int" , "int" }, []string {}):
214
+ return bfSwap
215
+ default :
216
+ return 0
217
+ }
218
+ }
0 commit comments