зеркало из https://github.com/golang/tools.git
90 строки
2.9 KiB
Go
90 строки
2.9 KiB
Go
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package typesinternal
|
||
|
|
||
|
import (
|
||
|
"go/types"
|
||
|
|
||
|
"golang.org/x/tools/internal/stdlib"
|
||
|
"golang.org/x/tools/internal/versions"
|
||
|
)
|
||
|
|
||
|
// TooNewStdSymbols computes the set of package-level symbols
|
||
|
// exported by pkg that are not available at the specified version.
|
||
|
// The result maps each symbol to its minimum version.
|
||
|
//
|
||
|
// The pkg is allowed to contain type errors.
|
||
|
func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string {
|
||
|
disallowed := make(map[types.Object]string)
|
||
|
|
||
|
// Pass 1: package-level symbols.
|
||
|
symbols := stdlib.PackageSymbols[pkg.Path()]
|
||
|
for _, sym := range symbols {
|
||
|
symver := sym.Version.String()
|
||
|
if versions.Before(version, symver) {
|
||
|
switch sym.Kind {
|
||
|
case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type:
|
||
|
disallowed[pkg.Scope().Lookup(sym.Name)] = symver
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Pass 2: fields and methods.
|
||
|
//
|
||
|
// We allow fields and methods if their associated type is
|
||
|
// disallowed, as otherwise we would report false positives
|
||
|
// for compatibility shims. Consider:
|
||
|
//
|
||
|
// //go:build go1.22
|
||
|
// type T struct { F std.Real } // correct new API
|
||
|
//
|
||
|
// //go:build !go1.22
|
||
|
// type T struct { F fake } // shim
|
||
|
// type fake struct { ... }
|
||
|
// func (fake) M () {}
|
||
|
//
|
||
|
// These alternative declarations of T use either the std.Real
|
||
|
// type, introduced in go1.22, or a fake type, for the field
|
||
|
// F. (The fakery could be arbitrarily deep, involving more
|
||
|
// nested fields and methods than are shown here.) Clients
|
||
|
// that use the compatibility shim T will compile with any
|
||
|
// version of go, whether older or newer than go1.22, but only
|
||
|
// the newer version will use the std.Real implementation.
|
||
|
//
|
||
|
// Now consider a reference to method M in new(T).F.M() in a
|
||
|
// module that requires a minimum of go1.21. The analysis may
|
||
|
// occur using a version of Go higher than 1.21, selecting the
|
||
|
// first version of T, so the method M is Real.M. This would
|
||
|
// spuriously cause the analyzer to report a reference to a
|
||
|
// too-new symbol even though this expression compiles just
|
||
|
// fine (with the fake implementation) using go1.21.
|
||
|
for _, sym := range symbols {
|
||
|
symVersion := sym.Version.String()
|
||
|
if !versions.Before(version, symVersion) {
|
||
|
continue // allowed
|
||
|
}
|
||
|
|
||
|
var obj types.Object
|
||
|
switch sym.Kind {
|
||
|
case stdlib.Field:
|
||
|
typename, name := sym.SplitField()
|
||
|
if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" {
|
||
|
obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name)
|
||
|
}
|
||
|
|
||
|
case stdlib.Method:
|
||
|
ptr, recvname, name := sym.SplitMethod()
|
||
|
if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" {
|
||
|
obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name)
|
||
|
}
|
||
|
}
|
||
|
if obj != nil {
|
||
|
disallowed[obj] = symVersion
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return disallowed
|
||
|
}
|