@@ -10,6 +10,7 @@ import (
10
10
"context"
11
11
"fmt"
12
12
"go/ast"
13
+ "go/build"
13
14
"go/constant"
14
15
"go/parser"
15
16
"go/printer"
@@ -28,6 +29,7 @@ import (
28
29
29
30
"golang.org/x/sync/errgroup"
30
31
"golang.org/x/tools/go/ast/astutil"
32
+ "golang.org/x/tools/gopls/internal/analysis/stdversion"
31
33
"golang.org/x/tools/gopls/internal/cache"
32
34
"golang.org/x/tools/gopls/internal/cache/metadata"
33
35
"golang.org/x/tools/gopls/internal/file"
@@ -37,6 +39,7 @@ import (
37
39
"golang.org/x/tools/gopls/internal/settings"
38
40
goplsastutil "golang.org/x/tools/gopls/internal/util/astutil"
39
41
"golang.org/x/tools/gopls/internal/util/safetoken"
42
+ "golang.org/x/tools/gopls/internal/util/slices"
40
43
"golang.org/x/tools/gopls/internal/util/typesutil"
41
44
"golang.org/x/tools/internal/aliases"
42
45
"golang.org/x/tools/internal/event"
@@ -194,6 +197,11 @@ type completer struct {
194
197
// file is the AST of the file associated with this completion request.
195
198
file * ast.File
196
199
200
+ // goversion is the version of Go in force in the file, as
201
+ // defined by x/tools/internal/versions. Empty if unknown.
202
+ // TODO(adonovan): with go1.22+ it should always be known.
203
+ goversion string
204
+
197
205
// (tokFile, pos) is the position at which the request was triggered.
198
206
tokFile * token.File
199
207
pos token.Pos
@@ -238,6 +246,13 @@ type completer struct {
238
246
// for deep completions.
239
247
methodSetCache map [methodSetKey ]* types.MethodSet
240
248
249
+ // tooNewSymbolsCache is a cache of
250
+ // [stdversion.DisallowedSymbols], recording for each std
251
+ // package which of its exported symbols are too new for
252
+ // the version of Go in force in the completion file.
253
+ // (The value is the minimum version in the form "go1.%d".)
254
+ tooNewSymbolsCache map [* types.Package ]map [types.Object ]string
255
+
241
256
// mapper converts the positions in the file from which the completion originated.
242
257
mapper * protocol.Mapper
243
258
@@ -257,6 +272,21 @@ type completer struct {
257
272
scopes []* types.Scope
258
273
}
259
274
275
+ // tooNew reports whether obj is a standard library symbol that is too
276
+ // new for the specified Go version.
277
+ func (c * completer ) tooNew (obj types.Object ) bool {
278
+ pkg := obj .Pkg ()
279
+ if pkg == nil {
280
+ return false // unsafe.Pointer or error.Error
281
+ }
282
+ disallowed , ok := c .tooNewSymbolsCache [pkg ]
283
+ if ! ok {
284
+ disallowed = stdversion .DisallowedSymbols (pkg , c .goversion )
285
+ c .tooNewSymbolsCache [pkg ] = disallowed
286
+ }
287
+ return disallowed [obj ] != ""
288
+ }
289
+
260
290
// funcInfo holds info about a function object.
261
291
type funcInfo struct {
262
292
// sig is the function declaration enclosing the position.
@@ -530,6 +560,12 @@ func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, p
530
560
scopes := golang .CollectScopes (pkg .GetTypesInfo (), path , pos )
531
561
scopes = append (scopes , pkg .GetTypes ().Scope (), types .Universe )
532
562
563
+ var goversion string // "" => no version check
564
+ // Prior go1.22, the behavior of FileVersion is not useful to us.
565
+ if slices .Contains (build .Default .ReleaseTags , "go1.22" ) {
566
+ goversion = versions .FileVersion (pkg .GetTypesInfo (), pgf .File ) // may be ""
567
+ }
568
+
533
569
opts := snapshot .Options ()
534
570
c := & completer {
535
571
pkg : pkg ,
@@ -544,6 +580,7 @@ func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, p
544
580
filename : fh .URI ().Path (),
545
581
tokFile : pgf .Tok ,
546
582
file : pgf .File ,
583
+ goversion : goversion ,
547
584
path : path ,
548
585
pos : pos ,
549
586
seen : make (map [types.Object ]bool ),
@@ -564,11 +601,12 @@ func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, p
564
601
completeFunctionCalls : opts .CompleteFunctionCalls ,
565
602
},
566
603
// default to a matcher that always matches
567
- matcher : prefixMatcher ("" ),
568
- methodSetCache : make (map [methodSetKey ]* types.MethodSet ),
569
- mapper : pgf .Mapper ,
570
- startTime : startTime ,
571
- scopes : scopes ,
604
+ matcher : prefixMatcher ("" ),
605
+ methodSetCache : make (map [methodSetKey ]* types.MethodSet ),
606
+ tooNewSymbolsCache : make (map [* types.Package ]map [types.Object ]string ),
607
+ mapper : pgf .Mapper ,
608
+ startTime : startTime ,
609
+ scopes : scopes ,
572
610
}
573
611
574
612
ctx , cancel := context .WithCancel (ctx )
@@ -1469,6 +1507,9 @@ func (c *completer) packageMembers(pkg *types.Package, score float64, imp *impor
1469
1507
scope := pkg .Scope ()
1470
1508
for _ , name := range scope .Names () {
1471
1509
obj := scope .Lookup (name )
1510
+ if c .tooNew (obj ) {
1511
+ continue // std symbol too new for file's Go version
1512
+ }
1472
1513
cb (candidate {
1473
1514
obj : obj ,
1474
1515
score : score ,
@@ -1506,6 +1547,11 @@ func (c *completer) methodsAndFields(typ types.Type, addressable bool, imp *impo
1506
1547
}
1507
1548
1508
1549
for i := 0 ; i < mset .Len (); i ++ {
1550
+ obj := mset .At (i ).Obj ()
1551
+ // to the other side of the cb() queue?
1552
+ if c .tooNew (obj ) {
1553
+ continue // std method too new for file's Go version
1554
+ }
1509
1555
cb (candidate {
1510
1556
obj : mset .At (i ).Obj (),
1511
1557
score : stdScore ,
@@ -1516,6 +1562,9 @@ func (c *completer) methodsAndFields(typ types.Type, addressable bool, imp *impo
1516
1562
1517
1563
// Add fields of T.
1518
1564
eachField (typ , func (v * types.Var ) {
1565
+ if c .tooNew (v ) {
1566
+ return // std field too new for file's Go version
1567
+ }
1519
1568
cb (candidate {
1520
1569
obj : v ,
1521
1570
score : stdScore - 0.01 ,
0 commit comments