--- /dev/null
+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
### 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.
"internal/goversion"
"internal/testenv"
"slices"
+ "sort"
"strings"
"sync"
"testing"
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)
+ }
+}
}
// 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)
// 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)
}
}
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}
}
}
}
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.
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,
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)
{
// 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)
}
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
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)
}
}
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)
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)
}
// 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.
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.
}
case *Var:
- if obj.isField {
+ if obj.IsField() {
buf.WriteString("field")
} else {
buf.WriteString("var")
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
// 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
}
// 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
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)
}
}
}
-// 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
}
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 ...")
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)
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
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 != "_" {
}
} 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)
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)
}
// 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") {
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.
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()
}
// 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
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 {
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:
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)
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
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()))
}
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)
"internal/goversion"
"internal/testenv"
"slices"
+ "sort"
"strings"
"sync"
"testing"
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)
+ }
+}
}
// 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)
// 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)
}
}
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}
}
}
}
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.
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,
{
// 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)
}
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
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)
}
}
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)
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)
}
// 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.
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.
}
case *Var:
- if obj.isField {
+ if obj.IsField() {
buf.WriteString("field")
} else {
buf.WriteString("var")
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
// 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
}
// 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
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)
}
}
}
-// 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
}
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 ...")
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)
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)
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
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 != "_" {
}
} 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)
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)