1
1
package lint
2
2
3
3
import (
4
+ "fmt"
4
5
"go/ast"
5
6
"go/importer"
6
7
"go/token"
@@ -31,7 +32,6 @@ type Package struct {
31
32
var (
32
33
trueValue = 1
33
34
falseValue = 2
34
- notSet = 3
35
35
36
36
go121 = goversion .Must (goversion .NewVersion ("1.21" ))
37
37
go122 = goversion .Must (goversion .NewVersion ("1.22" ))
@@ -111,6 +111,11 @@ func (p *Package) TypeCheck() error {
111
111
astFiles = append (astFiles , f .AST )
112
112
}
113
113
114
+ if anyFile == nil {
115
+ // this is unlikely to happen, but technically guarantees anyFile to not be nil
116
+ return fmt .Errorf ("no ast.File found" )
117
+ }
118
+
114
119
typesPkg , err := check (config , anyFile .AST .Name .Name , p .fset , astFiles , info )
115
120
116
121
// Remember the typechecking info, even if config.Check failed,
@@ -135,7 +140,7 @@ func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.
135
140
return config .Check (n , fset , astFiles , info )
136
141
}
137
142
138
- // TypeOf returns the type of an expression.
143
+ // TypeOf returns the type of expression.
139
144
func (p * Package ) TypeOf (expr ast.Expr ) types.Type {
140
145
if p .typesInfo == nil {
141
146
return nil
@@ -148,32 +153,72 @@ type walker struct {
148
153
has map [string ]int
149
154
}
150
155
156
+ // bitfield for which methods exist on each type.
157
+ const (
158
+ bfLen = 1 << iota
159
+ bfLess
160
+ bfSwap
161
+ )
162
+
151
163
func (w * walker ) Visit (n ast.Node ) ast.Visitor {
152
164
fn , ok := n .(* ast.FuncDecl )
153
165
if ! ok || fn .Recv == nil || len (fn .Recv .List ) == 0 {
154
166
return w
155
167
}
156
- // TODO(dsymonds): We could check the signature to be more precise.
168
+
157
169
recv := typeparams .ReceiverType (fn )
158
- if i , ok := w .nmap [fn .Name .Name ]; ok {
159
- w .has [recv ] |= i
170
+
171
+ // Ensure the method signature matches expectations.
172
+ switch fn .Name .Name {
173
+ case "Len" :
174
+ if fn .Type .Params .NumFields () == 0 && fn .Type .Results .NumFields () == 1 {
175
+ resultType := fn .Type .Results .List [0 ].Type
176
+ if _ , ok := resultType .(* ast.Ident ); ok && resultType .(* ast.Ident ).Name == "int" {
177
+ w .has [recv ] |= bfLen
178
+ }
179
+ }
180
+ case "Less" :
181
+ if fn .Type .Params .NumFields () == 2 && fn .Type .Results .NumFields () == 1 {
182
+ param1 := fn .Type .Params .List [0 ].Type
183
+ var param2 ast.Expr
184
+ if len (fn .Type .Params .List ) == 2 {
185
+ param2 = fn .Type .Params .List [1 ].Type
186
+ } else {
187
+ param2 = param1
188
+ }
189
+ resultType := fn .Type .Results .List [0 ].Type
190
+
191
+ // Ensure parameters have the same type and the result is a bool.
192
+ if typesEqual (param1 , param2 ) && isBool (resultType ) {
193
+ w .has [recv ] |= bfLess
194
+ }
195
+ }
196
+ case "Swap" :
197
+ if fn .Type .Params .NumFields () == 2 && fn .Type .Results .NumFields () == 0 {
198
+ w .has [recv ] |= bfSwap
199
+ }
160
200
}
161
201
return w
162
202
}
163
203
204
+ func typesEqual (a , b ast.Expr ) bool {
205
+ identA , okA := a .(* ast.Ident )
206
+ identB , okB := b .(* ast.Ident )
207
+ return okA && okB && identA .Name == identB .Name
208
+ }
209
+
210
+ func isBool (t ast.Expr ) bool {
211
+ ident , ok := t .(* ast.Ident )
212
+ return ok && ident .Name == "bool"
213
+ }
214
+
164
215
func (p * Package ) scanSortable () {
165
216
p .sortable = map [string ]bool {}
166
217
167
- // bitfield for which methods exist on each type.
168
- const (
169
- bfLen = 1 << iota
170
- bfLess
171
- bfSwap
172
- )
173
218
nmap := map [string ]int {"Len" : bfLen , "Less" : bfLess , "Swap" : bfSwap }
174
219
has := map [string ]int {}
175
220
for _ , f := range p .files {
176
- ast .Walk (& walker {nmap , has }, f .AST )
221
+ ast .Walk (& walker {nmap : nmap , has : has }, f .AST )
177
222
}
178
223
for typ , ms := range has {
179
224
if ms == bfLen | bfLess | bfSwap {
0 commit comments