go/types: add Var.Kind() VarKind method

This CL adds an enum type, VarKind, that discriminates among
the various kinds of Var, and adds setter/getter methods
for Var's kind field.

Beware: NewVar has a weaker postcondition: the Var objects it
returns are not completely initialized and require a call to
Var.SetKind. This should only affect importers.

No changes are needed to the export data, since the kind can
always be deduced from the context when decoding.

See CL 645656 for the corresponding x/tools changes.

+ test, relnote, API

Updates golang/go#70250

Change-Id: Icde86ad22a880cde6f50bc12bf38004a5c6a1025
Reviewed-on: https://go-review.googlesource.com/c/go/+/645115
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Alan Donovan 2025-01-28 13:44:41 -05:00
parent 2c16041423
commit 932a4a4bef
33 changed files with 320 additions and 90 deletions

16
api/go1.25.txt Normal file
View File

@ -0,0 +1,16 @@
pkg go/types, const FieldVar = 6 #70250
pkg go/types, const FieldVar VarKind #70250
pkg go/types, const LocalVar = 2 #70250
pkg go/types, const LocalVar VarKind #70250
pkg go/types, const PackageVar = 1 #70250
pkg go/types, const PackageVar VarKind #70250
pkg go/types, const ParamVar = 4 #70250
pkg go/types, const ParamVar VarKind #70250
pkg go/types, const RecvVar = 3 #70250
pkg go/types, const RecvVar VarKind #70250
pkg go/types, const ResultVar = 5 #70250
pkg go/types, const ResultVar VarKind #70250
pkg go/types, method (*Var) Kind() VarKind #70250
pkg go/types, method (*Var) SetKind(VarKind) #70250
pkg go/types, method (VarKind) String() string #70250
pkg go/types, type VarKind uint8 #70250

View File

@ -1,3 +1,10 @@
### Minor changes to the library {#minor_library_changes}
#### go/types
The `Var.Kind` method returns an enumeration of type `VarKind` that
classifies the variable (package-level, local, receiver, parameter,
result, or struct field). See issue #70250.
Callers of `NewVar` or `NewParam` are encouraged to call `Var.SetKind`
to ensure that this attribute is set correctly in all cases.

View File

@ -11,6 +11,7 @@ import (
"internal/goversion"
"internal/testenv"
"slices"
"sort"
"strings"
"sync"
"testing"
@ -3061,3 +3062,48 @@ func TestVersionWithoutPos(t *testing.T) {
t.Errorf("check error was %q, want substring %q", got, want)
}
}
func TestVarKind(t *testing.T) {
f := mustParse(`package p
var global int
type T struct { field int }
func (recv T) f(param int) (result int) {
var local int
local2 := 0
switch local3 := any(local).(type) {
default:
_ = local3
}
return local2
}
`)
pkg := NewPackage("p", "p")
info := &Info{Defs: make(map[*syntax.Name]Object)}
check := NewChecker(&Config{}, pkg, info)
if err := check.Files([]*syntax.File{f}); err != nil {
t.Fatal(err)
}
var got []string
for _, obj := range info.Defs {
if v, ok := obj.(*Var); ok {
got = append(got, fmt.Sprintf("%s: %v", v.Name(), v.Kind()))
}
}
sort.Strings(got)
want := []string{
"field: FieldVar",
"global: PackageVar",
"local2: LocalVar",
"local: LocalVar",
"param: ParamVar",
"recv: RecvVar",
"result: ResultVar",
}
if !slices.Equal(got, want) {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}

View File

@ -566,7 +566,7 @@ func (check *Checker) shortVarDecl(pos poser, lhs, rhs []syntax.Expr) {
}
// declare new variable
obj := NewVar(ident.Pos(), check.pkg, name, nil)
obj := newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
lhsVars[i] = obj
if name != "_" {
newVars = append(newVars, obj)
@ -577,7 +577,7 @@ func (check *Checker) shortVarDecl(pos poser, lhs, rhs []syntax.Expr) {
// create dummy variables where the lhs is invalid
for i, obj := range lhsVars {
if obj == nil {
lhsVars[i] = NewVar(lhs[i].Pos(), check.pkg, "_", nil)
lhsVars[i] = newVar(LocalVar, lhs[i].Pos(), check.pkg, "_", nil)
}
}

View File

@ -1032,13 +1032,13 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x *operand, id builtinId)
func makeSig(res Type, args ...Type) *Signature {
list := make([]*Var, len(args))
for i, param := range args {
list[i] = NewVar(nopos, nil, "", Default(param))
list[i] = NewParam(nopos, nil, "", Default(param))
}
params := NewTuple(list...)
var result *Tuple
if res != nil {
assert(!isUntyped(res))
result = NewTuple(NewVar(nopos, nil, "", res))
result = NewTuple(newVar(ResultVar, nopos, nil, "", res))
}
return &Signature{params: params, results: result}
}

View File

@ -94,7 +94,7 @@ func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *synt
}
}
gsig := NewSignatureType(nil, nil, nil, sig.params, sig.results, sig.variadic)
params = []*Var{NewVar(x.Pos(), check.pkg, "", gsig)}
params = []*Var{NewParam(x.Pos(), check.pkg, "", gsig)}
// The type of the argument operand is tsig, which is the type of the LHS in an assignment
// or the result type in a return statement. Create a pseudo-expression for that operand
// that makes sense when reported in error messages from infer, below.
@ -875,7 +875,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *TypeName
name = "_"
}
}
params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,

View File

@ -568,8 +568,8 @@ func (check *Checker) recordCommaOkTypesInSyntax(x syntax.Expr, t0, t1 Type) {
assert(tv.Type != nil) // should have been recorded already
pos := x.Pos()
tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1),
NewParam(pos, check.pkg, "", t0),
NewParam(pos, check.pkg, "", t1),
)
x.SetTypeInfo(tv)
p, _ := x.(*syntax.ParenExpr)

View File

@ -38,7 +38,7 @@ func TestContextHashCollisions(t *testing.T) {
{
// type unaryP = func[P any](_ P)
tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface)
params := NewTuple(NewVar(nopos, nil, "_", tparam))
params := NewTuple(NewParam(nopos, nil, "_", tparam))
unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false)
}

View File

@ -844,7 +844,7 @@ func (check *Checker) declStmt(list []syntax.Decl) {
lhs0 := make([]*Var, len(s.NameList))
for i, name := range s.NameList {
lhs0[i] = NewVar(name.Pos(), pkg, name.Value, nil)
lhs0[i] = newVar(LocalVar, name.Pos(), pkg, name.Value, nil)
}
// initialize all variables

View File

@ -42,7 +42,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
typ := (*Checker)(nil).newInterface()
for _, m := range methods {
if sig := m.typ.(*Signature); sig.recv == nil {
sig.recv = NewVar(m.pos, m.pkg, "", typ)
sig.recv = newVar(RecvVar, m.pos, m.pkg, "", typ)
}
}
@ -158,7 +158,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
recvTyp = named
}
}
sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
sig.recv = newVar(RecvVar, f.Name.Pos(), check.pkg, "", recvTyp)
m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
check.recordDef(f.Name, m)

View File

@ -628,7 +628,7 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...)
makeSig := func(typ Type) {
par := NewVar(nopos, nil, "", typ)
par := NewParam(nopos, nil, "", typ)
params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true)
}

View File

@ -331,28 +331,81 @@ func (obj *TypeName) IsAlias() bool {
// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
object
kind VarKind
embedded bool // if set, the variable is an embedded struct field, and name is the type name
isField bool // var is struct field
used bool // set if the variable was used
origin *Var // if non-nil, the Var from which this one was instantiated
}
// A VarKind discriminates the various kinds of variables.
type VarKind uint8
const (
_ VarKind = iota // (not meaningful)
PackageVar // a package-level variable
LocalVar // a local variable
RecvVar // a method receiver variable
ParamVar // a function parameter variable
ResultVar // a function result variable
FieldVar // a struct field
)
var varKindNames = [...]string{
0: "VarKind(0)",
PackageVar: "PackageVar",
LocalVar: "LocalVar",
RecvVar: "RecvVar",
ParamVar: "ParamVar",
ResultVar: "ResultVar",
FieldVar: "FieldVar",
}
func (kind VarKind) String() string {
if 0 <= kind && int(kind) < len(varKindNames) {
return varKindNames[kind]
}
return fmt.Sprintf("VarKind(%d)", kind)
}
// Kind reports what kind of variable v is.
func (v *Var) Kind() VarKind { return v.kind }
// SetKind sets the kind of the variable.
// It should be used only immediately after [NewVar] or [NewParam].
func (v *Var) SetKind(kind VarKind) { v.kind = kind }
// NewVar returns a new variable.
// The arguments set the attributes found with all Objects.
//
// The caller must subsequently call [Var.SetKind]
// if the desired Var is not of kind [PackageVar].
func NewVar(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
return newVar(PackageVar, pos, pkg, name, typ)
}
// NewParam returns a new variable representing a function parameter.
//
// The caller must subsequently call [Var.SetKind] if the desired Var
// is not of kind [ParamVar]: for example, [RecvVar] or [ResultVar].
func NewParam(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, used: true} // parameters are always 'used'
return newVar(ParamVar, pos, pkg, name, typ)
}
// NewField returns a new variable representing a struct field.
// For embedded fields, the name is the unqualified type name
// under which the field is accessible.
func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true}
v := newVar(FieldVar, pos, pkg, name, typ)
v.embedded = embedded
return v
}
// newVar returns a new variable.
// The arguments set the attributes found with all Objects.
func newVar(kind VarKind, pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
// Function parameters are always 'used'.
used := kind == RecvVar || kind == ParamVar || kind == ResultVar
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, kind: kind, used: used}
}
// Anonymous reports whether the variable is an embedded field.
@ -363,7 +416,7 @@ func (obj *Var) Anonymous() bool { return obj.embedded }
func (obj *Var) Embedded() bool { return obj.embedded }
// IsField reports whether the variable is a struct field.
func (obj *Var) IsField() bool { return obj.isField }
func (obj *Var) IsField() bool { return obj.kind == FieldVar }
// Origin returns the canonical Var for its receiver, i.e. the Var object
// recorded in Info.Defs.
@ -526,7 +579,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
}
case *Var:
if obj.isField {
if obj.IsField() {
buf.WriteString("field")
} else {
buf.WriteString("var")

View File

@ -105,8 +105,8 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) {
assert(tv.Type != nil) // should have been recorded already
pos := x.Pos()
tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1),
newVar(LocalVar, pos, check.pkg, "", t0),
newVar(LocalVar, pos, check.pkg, "", t1),
)
m[x] = tv
// if x is a parenthesized expression (p.X), update p.X

View File

@ -388,7 +388,7 @@ func (check *Checker) collectObjects() {
// declare all variables
values := syntax.UnpackListExpr(s.Values)
for i, name := range s.NameList {
obj := NewVar(name.Pos(), pkg, name.Value, nil)
obj := newVar(PackageVar, name.Pos(), pkg, name.Value, nil)
lhs[i] = obj
d := d1

View File

@ -117,8 +117,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
}
// collect ordinary and result parameters
pnames, params, variadic := check.collectParams(ftyp.ParamList, true)
rnames, results, _ := check.collectParams(ftyp.ResultList, false)
pnames, params, variadic := check.collectParams(ParamVar, ftyp.ParamList)
rnames, results, _ := check.collectParams(ResultVar, ftyp.ResultList)
// declare named receiver, ordinary, and result parameters
scopePos := syntax.EndPos(ftyp) // all parameter's scopes start after the signature
@ -258,13 +258,13 @@ func (check *Checker) collectRecv(rparam *syntax.Field, scopePos syntax.Pos) (*V
var recv *Var
if rname := rparam.Name; rname != nil && rname.Value != "" {
// named receiver
recv = NewParam(rname.Pos(), check.pkg, rname.Value, recvType)
recv = newVar(RecvVar, rname.Pos(), check.pkg, rname.Value, recvType)
// In this case, the receiver is declared by the caller
// because it must be declared after any type parameters
// (otherwise it might shadow one of them).
} else {
// anonymous receiver
recv = NewParam(rparam.Pos(), check.pkg, "", recvType)
recv = newVar(RecvVar, rparam.Pos(), check.pkg, "", recvType)
check.recordImplicit(rparam, recv)
}
@ -322,10 +322,11 @@ func (check *Checker) recordParenthesizedRecvTypes(expr syntax.Expr, typ Type) {
}
}
// collectParams collects (but does not declare) all parameters of list and returns
// the list of parameter names, corresponding parameter variables, and whether the
// parameter list is variadic. Anonymous parameters are recorded with nil names.
func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (names []*syntax.Name, params []*Var, variadic bool) {
// collectParams collects (but does not declare) all parameter/result
// variables of list and returns the list of names and corresponding
// variables, and whether the (parameter) list is variadic.
// Anonymous parameters are recorded with nil names.
func (check *Checker) collectParams(kind VarKind, list []*syntax.Field) (names []*syntax.Name, params []*Var, variadic bool) {
if list == nil {
return
}
@ -341,7 +342,7 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name
prev = ftype
if t, _ := ftype.(*syntax.DotsType); t != nil {
ftype = t.Elem
if variadicOk && i == len(list)-1 {
if kind == ParamVar && i == len(list)-1 {
variadic = true
} else {
check.error(t, InvalidSyntaxTree, "invalid use of ...")
@ -359,14 +360,14 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name
check.error(field.Name, InvalidSyntaxTree, "anonymous parameter")
// ok to continue
}
par := NewParam(field.Name.Pos(), check.pkg, name, typ)
par := newVar(kind, field.Name.Pos(), check.pkg, name, typ)
// named parameter is declared by caller
names = append(names, field.Name)
params = append(params, par)
named = true
} else {
// anonymous parameter
par := NewParam(field.Pos(), check.pkg, "", typ)
par := newVar(kind, field.Pos(), check.pkg, "", typ)
check.recordImplicit(field, par)
names = append(names, nil)
params = append(params, par)

View File

@ -793,7 +793,7 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
check.openScope(clause, "case")
// If lhs exists, declare a corresponding variable in the case-local scope.
if lhs != nil {
obj := NewVar(lhs.Pos(), check.pkg, lhs.Value, T)
obj := newVar(LocalVar, lhs.Pos(), check.pkg, lhs.Value, T)
check.declare(check.scope, nil, obj, clause.Colon)
check.recordImplicit(clause, obj)
// For the "declared and not used" error, all lhs variables act as
@ -904,7 +904,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
if ident, _ := lhs.(*identType); ident != nil {
// declare new variable
name := identName(ident)
obj = NewVar(ident.Pos(), check.pkg, name, nil)
obj = newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
check.recordDef(ident, obj)
// _ variables don't count as new variables
if name != "_" {
@ -912,7 +912,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
}
} else {
check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
obj = newVar(LocalVar, lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
assert(obj.typ == nil)

View File

@ -119,8 +119,8 @@ func defPredeclaredTypes() {
typ := NewNamed(obj, nil, nil)
// error.Error() string
recv := NewVar(nopos, nil, "", typ)
res := NewVar(nopos, nil, "", Typ[String])
recv := newVar(RecvVar, nopos, nil, "", typ)
res := newVar(ResultVar, nopos, nil, "", Typ[String])
sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)

View File

@ -264,7 +264,7 @@ func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
}
// Param = Name ["..."] Type .
func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) {
func (p *parser) parseParam(kind types.VarKind, pkg *types.Package) (param *types.Var, isVariadic bool) {
name := p.parseName()
// Ignore names invented for inlinable functions.
if strings.HasPrefix(name, "p.") || strings.HasPrefix(name, "r.") || strings.HasPrefix(name, "$ret") {
@ -289,13 +289,14 @@ func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bo
typ = types.NewSlice(typ)
}
param = types.NewParam(token.NoPos, pkg, name, typ)
param.SetKind(kind)
return
}
// Var = Name Type .
func (p *parser) parseVar(pkg *types.Package) *types.Var {
name := p.parseName()
v := types.NewVar(token.NoPos, pkg, name, p.parseType(pkg))
v := types.NewVar(token.NoPos, pkg, name, p.parseType(pkg)) // (types.PackageVar)
if name[0] == '.' || name[0] == '<' {
// This is an unexported variable,
// or a variable defined in a different package.
@ -589,10 +590,10 @@ func (p *parser) parseNamedType(nlist []any) types.Type {
p.expect('/')
}
p.expect('(')
receiver, _ := p.parseParam(pkg)
receiver, _ := p.parseParam(types.RecvVar, pkg)
p.expect(')')
name := p.parseName()
params, isVariadic := p.parseParamList(pkg)
params, isVariadic := p.parseParamList(types.ParamVar, pkg)
results := p.parseResultList(pkg)
p.skipInlineBody()
p.expectEOL()
@ -713,7 +714,7 @@ func (p *parser) parseStructType(pkg *types.Package, nlist []any) types.Type {
}
// ParamList = "(" [ { Parameter "," } Parameter ] ")" .
func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
func (p *parser) parseParamList(kind types.VarKind, pkg *types.Package) (*types.Tuple, bool) {
var list []*types.Var
isVariadic := false
@ -722,7 +723,7 @@ func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
if len(list) > 0 {
p.expect(',')
}
par, variadic := p.parseParam(pkg)
par, variadic := p.parseParam(kind, pkg)
list = append(list, par)
if variadic {
if isVariadic {
@ -745,10 +746,12 @@ func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
return nil
}
taa, _ := p.parseTypeAfterAngle(pkg)
return types.NewTuple(types.NewParam(token.NoPos, pkg, "", taa))
param := types.NewParam(token.NoPos, pkg, "", taa)
param.SetKind(types.ResultVar)
return types.NewTuple(param)
case '(':
params, _ := p.parseParamList(pkg)
params, _ := p.parseParamList(types.ResultVar, pkg)
return params
default:
@ -761,7 +764,7 @@ func (p *parser) parseFunctionType(pkg *types.Package, nlist []any) *types.Signa
t := new(types.Signature)
p.update(t, nlist)
params, isVariadic := p.parseParamList(pkg)
params, isVariadic := p.parseParamList(types.ParamVar, pkg)
results := p.parseResultList(pkg)
*t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic)

View File

@ -399,32 +399,34 @@ func (r *reader) interfaceType() *types.Interface {
func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
r.Sync(pkgbits.SyncSignature)
params := r.params()
results := r.params()
params := r.params(types.ParamVar)
results := r.params(types.ResultVar)
variadic := r.Bool()
return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
}
func (r *reader) params() *types.Tuple {
func (r *reader) params(kind types.VarKind) *types.Tuple {
r.Sync(pkgbits.SyncParams)
params := make([]*types.Var, r.Len())
for i := range params {
params[i] = r.param()
params[i] = r.param(kind)
}
return types.NewTuple(params...)
}
func (r *reader) param() *types.Var {
func (r *reader) param(kind types.VarKind) *types.Var {
r.Sync(pkgbits.SyncParam)
pos := r.pos()
pkg, name := r.localIdent()
typ := r.typ()
return types.NewParam(pos, pkg, name, typ)
param := types.NewParam(pos, pkg, name, typ)
param.SetKind(kind) // ∈ {Recv,Param,Result}Var
return param
}
// @@@ Objects
@ -528,6 +530,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
sig := fn.Signature()
recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
recv.SetKind(types.RecvVar)
methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignature(recv, sig.Params(), sig.Results(), sig.Variadic()))
}
@ -647,7 +650,7 @@ func (r *reader) method() *types.Func {
pkg, name := r.selector()
rparams := r.typeParamNames()
sig := r.signature(r.param(), rparams, nil)
sig := r.signature(r.param(types.RecvVar), rparams, nil)
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
return types.NewFunc(pos, pkg, name, sig)

View File

@ -14,6 +14,7 @@ import (
"internal/goversion"
"internal/testenv"
"slices"
"sort"
"strings"
"sync"
"testing"
@ -3124,3 +3125,49 @@ func TestVersionWithoutPos(t *testing.T) {
t.Errorf("check error was %q, want substring %q", got, want)
}
}
func TestVarKind(t *testing.T) {
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", `package p
var global int
type T struct { field int }
func (recv T) f(param int) (result int) {
var local int
local2 := 0
switch local3 := any(local).(type) {
default:
_ = local3
}
return local2
}
`, 0)
pkg := NewPackage("p", "p")
info := &Info{Defs: make(map[*ast.Ident]Object)}
check := NewChecker(&Config{}, fset, pkg, info)
if err := check.Files([]*ast.File{f}); err != nil {
t.Fatal(err)
}
var got []string
for _, obj := range info.Defs {
if v, ok := obj.(*Var); ok {
got = append(got, fmt.Sprintf("%s: %v", v.Name(), v.Kind()))
}
}
sort.Strings(got)
want := []string{
"field: FieldVar",
"global: PackageVar",
"local2: LocalVar",
"local: LocalVar",
"param: ParamVar",
"recv: RecvVar",
"result: ResultVar",
}
if !slices.Equal(got, want) {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}

View File

@ -569,7 +569,7 @@ func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
}
// declare new variable
obj := NewVar(ident.Pos(), check.pkg, name, nil)
obj := newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
lhsVars[i] = obj
if name != "_" {
newVars = append(newVars, obj)
@ -580,7 +580,7 @@ func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
// create dummy variables where the lhs is invalid
for i, obj := range lhsVars {
if obj == nil {
lhsVars[i] = NewVar(lhs[i].Pos(), check.pkg, "_", nil)
lhsVars[i] = newVar(LocalVar, lhs[i].Pos(), check.pkg, "_", nil)
}
}

View File

@ -1035,13 +1035,13 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x *operand, id builtinId)
func makeSig(res Type, args ...Type) *Signature {
list := make([]*Var, len(args))
for i, param := range args {
list[i] = NewVar(nopos, nil, "", Default(param))
list[i] = NewParam(nopos, nil, "", Default(param))
}
params := NewTuple(list...)
var result *Tuple
if res != nil {
assert(!isUntyped(res))
result = NewTuple(NewVar(nopos, nil, "", res))
result = NewTuple(newVar(ResultVar, nopos, nil, "", res))
}
return &Signature{params: params, results: result}
}

View File

@ -95,7 +95,7 @@ func (check *Checker) funcInst(T *target, pos token.Pos, x *operand, ix *indexed
}
}
gsig := NewSignatureType(nil, nil, nil, sig.params, sig.results, sig.variadic)
params = []*Var{NewVar(x.Pos(), check.pkg, "", gsig)}
params = []*Var{NewParam(x.Pos(), check.pkg, "", gsig)}
// The type of the argument operand is tsig, which is the type of the LHS in an assignment
// or the result type in a return statement. Create a pseudo-expression for that operand
// that makes sense when reported in error messages from infer, below.
@ -878,7 +878,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr, def *TypeName, w
name = "_"
}
}
params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,

View File

@ -41,7 +41,7 @@ func TestContextHashCollisions(t *testing.T) {
{
// type unaryP = func[P any](_ P)
tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface)
params := NewTuple(NewVar(nopos, nil, "_", tparam))
params := NewTuple(NewParam(nopos, nil, "_", tparam))
unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false)
}

View File

@ -909,7 +909,7 @@ func (check *Checker) declStmt(d ast.Decl) {
lhs0 := make([]*Var, len(d.spec.Names))
for i, name := range d.spec.Names {
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
lhs0[i] = newVar(LocalVar, name.Pos(), pkg, name.Name, nil)
}
// initialize all variables

View File

@ -59,7 +59,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
typ := (*Checker)(nil).newInterface()
for _, m := range methods {
if sig := m.typ.(*Signature); sig.recv == nil {
sig.recv = NewVar(m.pos, m.pkg, "", typ)
sig.recv = newVar(RecvVar, m.pos, m.pkg, "", typ)
}
}
@ -206,7 +206,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
recvTyp = named
}
}
sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp)
sig.recv = newVar(RecvVar, name.Pos(), check.pkg, "", recvTyp)
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.recordDef(name, m)

View File

@ -638,7 +638,7 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...)
makeSig := func(typ Type) {
par := NewVar(nopos, nil, "", typ)
par := NewParam(nopos, nil, "", typ)
params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true)
}

View File

@ -334,28 +334,81 @@ func (obj *TypeName) IsAlias() bool {
// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
object
kind VarKind
embedded bool // if set, the variable is an embedded struct field, and name is the type name
isField bool // var is struct field
used bool // set if the variable was used
origin *Var // if non-nil, the Var from which this one was instantiated
}
// A VarKind discriminates the various kinds of variables.
type VarKind uint8
const (
_ VarKind = iota // (not meaningful)
PackageVar // a package-level variable
LocalVar // a local variable
RecvVar // a method receiver variable
ParamVar // a function parameter variable
ResultVar // a function result variable
FieldVar // a struct field
)
var varKindNames = [...]string{
0: "VarKind(0)",
PackageVar: "PackageVar",
LocalVar: "LocalVar",
RecvVar: "RecvVar",
ParamVar: "ParamVar",
ResultVar: "ResultVar",
FieldVar: "FieldVar",
}
func (kind VarKind) String() string {
if 0 <= kind && int(kind) < len(varKindNames) {
return varKindNames[kind]
}
return fmt.Sprintf("VarKind(%d)", kind)
}
// Kind reports what kind of variable v is.
func (v *Var) Kind() VarKind { return v.kind }
// SetKind sets the kind of the variable.
// It should be used only immediately after [NewVar] or [NewParam].
func (v *Var) SetKind(kind VarKind) { v.kind = kind }
// NewVar returns a new variable.
// The arguments set the attributes found with all Objects.
//
// The caller must subsequently call [Var.SetKind]
// if the desired Var is not of kind [PackageVar].
func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
return newVar(PackageVar, pos, pkg, name, typ)
}
// NewParam returns a new variable representing a function parameter.
//
// The caller must subsequently call [Var.SetKind] if the desired Var
// is not of kind [ParamVar]: for example, [RecvVar] or [ResultVar].
func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, used: true} // parameters are always 'used'
return newVar(ParamVar, pos, pkg, name, typ)
}
// NewField returns a new variable representing a struct field.
// For embedded fields, the name is the unqualified type name
// under which the field is accessible.
func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool) *Var {
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true}
v := newVar(FieldVar, pos, pkg, name, typ)
v.embedded = embedded
return v
}
// newVar returns a new variable.
// The arguments set the attributes found with all Objects.
func newVar(kind VarKind, pos token.Pos, pkg *Package, name string, typ Type) *Var {
// Function parameters are always 'used'.
used := kind == RecvVar || kind == ParamVar || kind == ResultVar
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, kind: kind, used: used}
}
// Anonymous reports whether the variable is an embedded field.
@ -366,7 +419,7 @@ func (obj *Var) Anonymous() bool { return obj.embedded }
func (obj *Var) Embedded() bool { return obj.embedded }
// IsField reports whether the variable is a struct field.
func (obj *Var) IsField() bool { return obj.isField }
func (obj *Var) IsField() bool { return obj.kind == FieldVar }
// Origin returns the canonical Var for its receiver, i.e. the Var object
// recorded in Info.Defs.
@ -529,7 +582,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
}
case *Var:
if obj.isField {
if obj.IsField() {
buf.WriteString("field")
} else {
buf.WriteString("var")

View File

@ -108,8 +108,8 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) {
assert(tv.Type != nil) // should have been recorded already
pos := x.Pos()
tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1),
newVar(LocalVar, pos, check.pkg, "", t0),
newVar(LocalVar, pos, check.pkg, "", t1),
)
m[x] = tv
// if x is a parenthesized expression (p.X), update p.X

View File

@ -379,7 +379,7 @@ func (check *Checker) collectObjects() {
// declare all variables
for i, name := range d.spec.Names {
obj := NewVar(name.Pos(), pkg, name.Name, nil)
obj := newVar(PackageVar, name.Pos(), pkg, name.Name, nil)
lhs[i] = obj
di := d1

View File

@ -140,8 +140,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
}
// collect ordinary and result parameters
pnames, params, variadic := check.collectParams(ftyp.Params, true)
rnames, results, _ := check.collectParams(ftyp.Results, false)
pnames, params, variadic := check.collectParams(ParamVar, ftyp.Params)
rnames, results, _ := check.collectParams(ResultVar, ftyp.Results)
// declare named receiver, ordinary, and result parameters
scopePos := ftyp.End() // all parameter's scopes start after the signature
@ -290,13 +290,13 @@ func (check *Checker) collectRecv(rparam *ast.Field, scopePos token.Pos) (*Var,
var recv *Var
if rname != nil && rname.Name != "" {
// named receiver
recv = NewParam(rname.Pos(), check.pkg, rname.Name, recvType)
recv = newVar(RecvVar, rname.Pos(), check.pkg, rname.Name, recvType)
// In this case, the receiver is declared by the caller
// because it must be declared after any type parameters
// (otherwise it might shadow one of them).
} else {
// anonymous receiver
recv = NewParam(rparam.Pos(), check.pkg, "", recvType)
recv = newVar(RecvVar, rparam.Pos(), check.pkg, "", recvType)
check.recordImplicit(rparam, recv)
}
@ -350,10 +350,11 @@ func (check *Checker) recordParenthesizedRecvTypes(expr ast.Expr, typ Type) {
}
}
// collectParams collects (but does not declare) all parameters of list and returns
// the list of parameter names, corresponding parameter variables, and whether the
// parameter list is variadic. Anonymous parameters are recorded with nil names.
func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names []*ast.Ident, params []*Var, variadic bool) {
// collectParams collects (but does not declare) all parameter/result
// variables of list and returns the list of names and corresponding
// variables, and whether the (parameter) list is variadic.
// Anonymous parameters are recorded with nil names.
func (check *Checker) collectParams(kind VarKind, list *ast.FieldList) (names []*ast.Ident, params []*Var, variadic bool) {
if list == nil {
return
}
@ -363,7 +364,7 @@ func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names
ftype := field.Type
if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
if kind == ParamVar && i == len(list.List)-1 && len(field.Names) <= 1 {
variadic = true
} else {
check.softErrorf(t, InvalidSyntaxTree, "invalid use of ...")
@ -380,7 +381,7 @@ func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names
check.error(name, InvalidSyntaxTree, "anonymous parameter")
// ok to continue
}
par := NewParam(name.Pos(), check.pkg, name.Name, typ)
par := newVar(kind, name.Pos(), check.pkg, name.Name, typ)
// named parameter is declared by caller
names = append(names, name)
params = append(params, par)
@ -388,7 +389,7 @@ func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names
named = true
} else {
// anonymous parameter
par := NewParam(ftype.Pos(), check.pkg, "", typ)
par := newVar(kind, ftype.Pos(), check.pkg, "", typ)
check.recordImplicit(field, par)
names = append(names, nil)
params = append(params, par)

View File

@ -752,7 +752,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.openScope(clause, "case")
// If lhs exists, declare a corresponding variable in the case-local scope.
if lhs != nil {
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
obj := newVar(LocalVar, lhs.Pos(), check.pkg, lhs.Name, T)
check.declare(check.scope, nil, obj, clause.Colon)
check.recordImplicit(clause, obj)
// For the "declared and not used" error, all lhs variables act as
@ -922,7 +922,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
if ident, _ := lhs.(*identType); ident != nil {
// declare new variable
name := identName(ident)
obj = NewVar(ident.Pos(), check.pkg, name, nil)
obj = newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
check.recordDef(ident, obj)
// _ variables don't count as new variables
if name != "_" {
@ -930,7 +930,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
}
} else {
check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
obj = newVar(LocalVar, lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
assert(obj.typ == nil)

View File

@ -122,8 +122,8 @@ func defPredeclaredTypes() {
typ := NewNamed(obj, nil, nil)
// error.Error() string
recv := NewVar(nopos, nil, "", typ)
res := NewVar(nopos, nil, "", Typ[String])
recv := newVar(RecvVar, nopos, nil, "", typ)
res := newVar(ResultVar, nopos, nil, "", Typ[String])
sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)