@@ -15,8 +15,7 @@ import (
15
15
"golang.org/x/tools/go/analysis"
16
16
"golang.org/x/tools/go/analysis/passes/inspect"
17
17
"golang.org/x/tools/go/ast/inspector"
18
- "golang.org/x/tools/gopls/internal/util/slices"
19
- "golang.org/x/tools/internal/stdlib"
18
+ "golang.org/x/tools/internal/typesinternal"
20
19
"golang.org/x/tools/internal/versions"
21
20
)
22
21
@@ -35,32 +34,34 @@ through a type alias that is guarded by a Go version constraint.
35
34
`
36
35
37
36
var Analyzer = & analysis.Analyzer {
38
- Name : "stdversion" ,
39
- Doc : Doc ,
40
- Requires : []* analysis.Analyzer {inspect .Analyzer },
41
- Run : run ,
37
+ Name : "stdversion" ,
38
+ Doc : Doc ,
39
+ Requires : []* analysis.Analyzer {inspect .Analyzer },
40
+ URL : "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/stdversion" ,
41
+ RunDespiteErrors : true ,
42
+ Run : run ,
42
43
}
43
44
44
45
func run (pass * analysis.Pass ) (any , error ) {
45
46
// Prior to go1.22, versions.FileVersion returns only the
46
47
// toolchain version, which is of no use to us, so
47
48
// disable this analyzer on earlier versions.
48
- if ! slices . Contains (build .Default .ReleaseTags , "go1.22" ) {
49
+ if ! slicesContains (build .Default .ReleaseTags , "go1.22" ) {
49
50
return nil , nil
50
51
}
51
52
52
- // disallowedSymbolsMemo returns the set of standard library symbols
53
+ // disallowedSymbols returns the set of standard library symbols
53
54
// in a given package that are disallowed at the specified Go version.
54
55
type key struct {
55
56
pkg * types.Package
56
57
version string
57
58
}
58
59
memo := make (map [key ]map [types.Object ]string ) // records symbol's minimum Go version
59
- disallowedSymbolsMemo := func (pkg * types.Package , version string ) map [types.Object ]string {
60
+ disallowedSymbols := func (pkg * types.Package , version string ) map [types.Object ]string {
60
61
k := key {pkg , version }
61
62
disallowed , ok := memo [k ]
62
63
if ! ok {
63
- disallowed = DisallowedSymbols (pkg , version )
64
+ disallowed = typesinternal . TooNewStdSymbols (pkg , version )
64
65
memo [k ] = disallowed
65
66
}
66
67
return disallowed
@@ -91,7 +92,7 @@ func run(pass *analysis.Pass) (any, error) {
91
92
case * ast.Ident :
92
93
if fileVersion != "" {
93
94
if obj , ok := pass .TypesInfo .Uses [n ]; ok && obj .Pkg () != nil {
94
- disallowed := disallowedSymbolsMemo (obj .Pkg (), fileVersion )
95
+ disallowed := disallowedSymbols (obj .Pkg (), fileVersion )
95
96
if minVersion , ok := disallowed [origin (obj )]; ok {
96
97
noun := "module"
97
98
if fileVersion != pkgVersion {
@@ -107,86 +108,7 @@ func run(pass *analysis.Pass) (any, error) {
107
108
return nil , nil
108
109
}
109
110
110
- // DisallowedSymbols computes the set of package-level symbols
111
- // exported by pkg that are not available at the specified version.
112
- // The result maps each symbol to its minimum version.
113
- //
114
- // (It is exported for use in gopls' completion.)
115
- func DisallowedSymbols (pkg * types.Package , version string ) map [types.Object ]string {
116
- disallowed := make (map [types.Object ]string )
117
-
118
- // Pass 1: package-level symbols.
119
- symbols := stdlib .PackageSymbols [pkg .Path ()]
120
- for _ , sym := range symbols {
121
- symver := sym .Version .String ()
122
- if versions .Before (version , symver ) {
123
- switch sym .Kind {
124
- case stdlib .Func , stdlib .Var , stdlib .Const , stdlib .Type :
125
- disallowed [pkg .Scope ().Lookup (sym .Name )] = symver
126
- }
127
- }
128
- }
129
-
130
- // Pass 2: fields and methods.
131
- //
132
- // We allow fields and methods if their associated type is
133
- // disallowed, as otherwise we would report false positives
134
- // for compatibility shims. Consider:
135
- //
136
- // //go:build go1.22
137
- // type T struct { F std.Real } // correct new API
138
- //
139
- // //go:build !go1.22
140
- // type T struct { F fake } // shim
141
- // type fake struct { ... }
142
- // func (fake) M () {}
143
- //
144
- // These alternative declarations of T use either the std.Real
145
- // type, introduced in go1.22, or a fake type, for the field
146
- // F. (The fakery could be arbitrarily deep, involving more
147
- // nested fields and methods than are shown here.) Clients
148
- // that use the compatibility shim T will compile with any
149
- // version of go, whether older or newer than go1.22, but only
150
- // the newer version will use the std.Real implementation.
151
- //
152
- // Now consider a reference to method M in new(T).F.M() in a
153
- // module that requires a minimum of go1.21. The analysis may
154
- // occur using a version of Go higher than 1.21, selecting the
155
- // first version of T, so the method M is Real.M. This would
156
- // spuriously cause the analyzer to report a reference to a
157
- // too-new symbol even though this expression compiles just
158
- // fine (with the fake implementation) using go1.21.
159
- for _ , sym := range symbols {
160
- symVersion := sym .Version .String ()
161
- if ! versions .Before (version , symVersion ) {
162
- continue // allowed
163
- }
164
-
165
- var obj types.Object
166
- switch sym .Kind {
167
- case stdlib .Field :
168
- typename , name := sym .SplitField ()
169
- t := pkg .Scope ().Lookup (typename )
170
- if disallowed [t ] == "" {
171
- obj , _ , _ = types .LookupFieldOrMethod (t .Type (), false , pkg , name )
172
- }
173
-
174
- case stdlib .Method :
175
- ptr , recvname , name := sym .SplitMethod ()
176
- t := pkg .Scope ().Lookup (recvname )
177
- if disallowed [t ] == "" {
178
- obj , _ , _ = types .LookupFieldOrMethod (t .Type (), ptr , pkg , name )
179
- }
180
- }
181
- if obj != nil {
182
- disallowed [obj ] = symVersion
183
- }
184
- }
185
-
186
- return disallowed
187
- }
188
-
189
- // Reduced from ../../golang/util.go. Good enough for now.
111
+ // Reduced from x/tools/gopls/internal/golang/util.go. Good enough for now.
190
112
// TODO(adonovan): use ast.IsGenerated in go1.21.
191
113
func isGenerated (f * ast.File ) bool {
192
114
for _ , group := range f .Comments {
@@ -218,3 +140,13 @@ func origin(obj types.Object) types.Object {
218
140
}
219
141
return obj
220
142
}
143
+
144
+ // TODO(adonovan): use go1.21 slices.Contains.
145
+ func slicesContains [S ~ []E , E comparable ](slice S , x E ) bool {
146
+ for _ , elem := range slice {
147
+ if elem == x {
148
+ return true
149
+ }
150
+ }
151
+ return false
152
+ }
0 commit comments