golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/typesinternal/toonew.go (about) 1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package typesinternal 6 7 import ( 8 "go/types" 9 10 "golang.org/x/tools/internal/stdlib" 11 "golang.org/x/tools/internal/versions" 12 ) 13 14 // TooNewStdSymbols computes the set of package-level symbols 15 // exported by pkg that are not available at the specified version. 16 // The result maps each symbol to its minimum version. 17 // 18 // The pkg is allowed to contain type errors. 19 func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string { 20 disallowed := make(map[types.Object]string) 21 22 // Pass 1: package-level symbols. 23 symbols := stdlib.PackageSymbols[pkg.Path()] 24 for _, sym := range symbols { 25 symver := sym.Version.String() 26 if versions.Before(version, symver) { 27 switch sym.Kind { 28 case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type: 29 disallowed[pkg.Scope().Lookup(sym.Name)] = symver 30 } 31 } 32 } 33 34 // Pass 2: fields and methods. 35 // 36 // We allow fields and methods if their associated type is 37 // disallowed, as otherwise we would report false positives 38 // for compatibility shims. Consider: 39 // 40 // //go:build go1.22 41 // type T struct { F std.Real } // correct new API 42 // 43 // //go:build !go1.22 44 // type T struct { F fake } // shim 45 // type fake struct { ... } 46 // func (fake) M () {} 47 // 48 // These alternative declarations of T use either the std.Real 49 // type, introduced in go1.22, or a fake type, for the field 50 // F. (The fakery could be arbitrarily deep, involving more 51 // nested fields and methods than are shown here.) Clients 52 // that use the compatibility shim T will compile with any 53 // version of go, whether older or newer than go1.22, but only 54 // the newer version will use the std.Real implementation. 55 // 56 // Now consider a reference to method M in new(T).F.M() in a 57 // module that requires a minimum of go1.21. The analysis may 58 // occur using a version of Go higher than 1.21, selecting the 59 // first version of T, so the method M is Real.M. This would 60 // spuriously cause the analyzer to report a reference to a 61 // too-new symbol even though this expression compiles just 62 // fine (with the fake implementation) using go1.21. 63 for _, sym := range symbols { 64 symVersion := sym.Version.String() 65 if !versions.Before(version, symVersion) { 66 continue // allowed 67 } 68 69 var obj types.Object 70 switch sym.Kind { 71 case stdlib.Field: 72 typename, name := sym.SplitField() 73 if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" { 74 obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name) 75 } 76 77 case stdlib.Method: 78 ptr, recvname, name := sym.SplitMethod() 79 if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" { 80 obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name) 81 } 82 } 83 if obj != nil { 84 disallowed[obj] = symVersion 85 } 86 } 87 88 return disallowed 89 }