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} ### 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/goversion"
"internal/testenv" "internal/testenv"
"slices" "slices"
"sort"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -3061,3 +3062,48 @@ func TestVersionWithoutPos(t *testing.T) {
t.Errorf("check error was %q, want substring %q", got, want) 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 // declare new variable
obj := NewVar(ident.Pos(), check.pkg, name, nil) obj := newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
lhsVars[i] = obj lhsVars[i] = obj
if name != "_" { if name != "_" {
newVars = append(newVars, obj) 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 // create dummy variables where the lhs is invalid
for i, obj := range lhsVars { for i, obj := range lhsVars {
if obj == nil { 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 { func makeSig(res Type, args ...Type) *Signature {
list := make([]*Var, len(args)) list := make([]*Var, len(args))
for i, param := range args { for i, param := range args {
list[i] = NewVar(nopos, nil, "", Default(param)) list[i] = NewParam(nopos, nil, "", Default(param))
} }
params := NewTuple(list...) params := NewTuple(list...)
var result *Tuple var result *Tuple
if res != nil { if res != nil {
assert(!isUntyped(res)) assert(!isUntyped(res))
result = NewTuple(NewVar(nopos, nil, "", res)) result = NewTuple(newVar(ResultVar, nopos, nil, "", res))
} }
return &Signature{params: params, results: result} 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) 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 // 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 // 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. // 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 = "_" 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.mode = value
x.typ = &Signature{ x.typ = &Signature{
tparams: sig.tparams, 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 assert(tv.Type != nil) // should have been recorded already
pos := x.Pos() pos := x.Pos()
tv.Type = NewTuple( tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0), NewParam(pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1), NewParam(pos, check.pkg, "", t1),
) )
x.SetTypeInfo(tv) x.SetTypeInfo(tv)
p, _ := x.(*syntax.ParenExpr) p, _ := x.(*syntax.ParenExpr)

View File

@ -38,7 +38,7 @@ func TestContextHashCollisions(t *testing.T) {
{ {
// type unaryP = func[P any](_ P) // type unaryP = func[P any](_ P)
tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface) 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) 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)) lhs0 := make([]*Var, len(s.NameList))
for i, name := range 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 // initialize all variables

View File

@ -42,7 +42,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
typ := (*Checker)(nil).newInterface() typ := (*Checker)(nil).newInterface()
for _, m := range methods { for _, m := range methods {
if sig := m.typ.(*Signature); sig.recv == nil { 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 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) m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
check.recordDef(f.Name, m) check.recordDef(f.Name, m)

View File

@ -628,7 +628,7 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) { func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...) // makeSig makes the signature func(typ...)
makeSig := func(typ Type) { makeSig := func(typ Type) {
par := NewVar(nopos, nil, "", typ) par := NewParam(nopos, nil, "", typ)
params := NewTuple(par) params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true) 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). // A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct { type Var struct {
object object
kind VarKind
embedded bool // if set, the variable is an embedded struct field, and name is the type name 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 used bool // set if the variable was used
origin *Var // if non-nil, the Var from which this one was instantiated 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. // NewVar returns a new variable.
// The arguments set the attributes found with all Objects. // 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 { 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. // 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 { 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. // NewField returns a new variable representing a struct field.
// For embedded fields, the name is the unqualified type name // For embedded fields, the name is the unqualified type name
// under which the field is accessible. // under which the field is accessible.
func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { 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. // 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 } func (obj *Var) Embedded() bool { return obj.embedded }
// IsField reports whether the variable is a struct field. // 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 // Origin returns the canonical Var for its receiver, i.e. the Var object
// recorded in Info.Defs. // recorded in Info.Defs.
@ -526,7 +579,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
} }
case *Var: case *Var:
if obj.isField { if obj.IsField() {
buf.WriteString("field") buf.WriteString("field")
} else { } else {
buf.WriteString("var") 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 assert(tv.Type != nil) // should have been recorded already
pos := x.Pos() pos := x.Pos()
tv.Type = NewTuple( tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0), newVar(LocalVar, pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1), newVar(LocalVar, pos, check.pkg, "", t1),
) )
m[x] = tv m[x] = tv
// if x is a parenthesized expression (p.X), update p.X // if x is a parenthesized expression (p.X), update p.X

View File

@ -388,7 +388,7 @@ func (check *Checker) collectObjects() {
// declare all variables // declare all variables
values := syntax.UnpackListExpr(s.Values) values := syntax.UnpackListExpr(s.Values)
for i, name := range s.NameList { 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 lhs[i] = obj
d := d1 d := d1

View File

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

View File

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

View File

@ -119,8 +119,8 @@ func defPredeclaredTypes() {
typ := NewNamed(obj, nil, nil) typ := NewNamed(obj, nil, nil)
// error.Error() string // error.Error() string
recv := NewVar(nopos, nil, "", typ) recv := newVar(RecvVar, nopos, nil, "", typ)
res := NewVar(nopos, nil, "", Typ[String]) res := newVar(ResultVar, nopos, nil, "", Typ[String])
sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false) sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig) 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 . // 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() name := p.parseName()
// Ignore names invented for inlinable functions. // Ignore names invented for inlinable functions.
if strings.HasPrefix(name, "p.") || strings.HasPrefix(name, "r.") || strings.HasPrefix(name, "$ret") { 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) typ = types.NewSlice(typ)
} }
param = types.NewParam(token.NoPos, pkg, name, typ) param = types.NewParam(token.NoPos, pkg, name, typ)
param.SetKind(kind)
return return
} }
// Var = Name Type . // Var = Name Type .
func (p *parser) parseVar(pkg *types.Package) *types.Var { func (p *parser) parseVar(pkg *types.Package) *types.Var {
name := p.parseName() 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] == '<' { if name[0] == '.' || name[0] == '<' {
// This is an unexported variable, // This is an unexported variable,
// or a variable defined in a different package. // or a variable defined in a different package.
@ -589,10 +590,10 @@ func (p *parser) parseNamedType(nlist []any) types.Type {
p.expect('/') p.expect('/')
} }
p.expect('(') p.expect('(')
receiver, _ := p.parseParam(pkg) receiver, _ := p.parseParam(types.RecvVar, pkg)
p.expect(')') p.expect(')')
name := p.parseName() name := p.parseName()
params, isVariadic := p.parseParamList(pkg) params, isVariadic := p.parseParamList(types.ParamVar, pkg)
results := p.parseResultList(pkg) results := p.parseResultList(pkg)
p.skipInlineBody() p.skipInlineBody()
p.expectEOL() p.expectEOL()
@ -713,7 +714,7 @@ func (p *parser) parseStructType(pkg *types.Package, nlist []any) types.Type {
} }
// ParamList = "(" [ { Parameter "," } Parameter ] ")" . // 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 var list []*types.Var
isVariadic := false isVariadic := false
@ -722,7 +723,7 @@ func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
if len(list) > 0 { if len(list) > 0 {
p.expect(',') p.expect(',')
} }
par, variadic := p.parseParam(pkg) par, variadic := p.parseParam(kind, pkg)
list = append(list, par) list = append(list, par)
if variadic { if variadic {
if isVariadic { if isVariadic {
@ -745,10 +746,12 @@ func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
return nil return nil
} }
taa, _ := p.parseTypeAfterAngle(pkg) 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 '(': case '(':
params, _ := p.parseParamList(pkg) params, _ := p.parseParamList(types.ResultVar, pkg)
return params return params
default: default:
@ -761,7 +764,7 @@ func (p *parser) parseFunctionType(pkg *types.Package, nlist []any) *types.Signa
t := new(types.Signature) t := new(types.Signature)
p.update(t, nlist) p.update(t, nlist)
params, isVariadic := p.parseParamList(pkg) params, isVariadic := p.parseParamList(types.ParamVar, pkg)
results := p.parseResultList(pkg) results := p.parseResultList(pkg)
*t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic) *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 { func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
r.Sync(pkgbits.SyncSignature) r.Sync(pkgbits.SyncSignature)
params := r.params() params := r.params(types.ParamVar)
results := r.params() results := r.params(types.ResultVar)
variadic := r.Bool() variadic := r.Bool()
return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic) 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) r.Sync(pkgbits.SyncParams)
params := make([]*types.Var, r.Len()) params := make([]*types.Var, r.Len())
for i := range params { for i := range params {
params[i] = r.param() params[i] = r.param(kind)
} }
return types.NewTuple(params...) return types.NewTuple(params...)
} }
func (r *reader) param() *types.Var { func (r *reader) param(kind types.VarKind) *types.Var {
r.Sync(pkgbits.SyncParam) r.Sync(pkgbits.SyncParam)
pos := r.pos() pos := r.pos()
pkg, name := r.localIdent() pkg, name := r.localIdent()
typ := r.typ() 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 // @@@ Objects
@ -528,6 +530,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
sig := fn.Signature() sig := fn.Signature()
recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named) 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())) 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() pkg, name := r.selector()
rparams := r.typeParamNames() 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. _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
return types.NewFunc(pos, pkg, name, sig) return types.NewFunc(pos, pkg, name, sig)

View File

@ -14,6 +14,7 @@ import (
"internal/goversion" "internal/goversion"
"internal/testenv" "internal/testenv"
"slices" "slices"
"sort"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -3124,3 +3125,49 @@ func TestVersionWithoutPos(t *testing.T) {
t.Errorf("check error was %q, want substring %q", got, want) 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 // declare new variable
obj := NewVar(ident.Pos(), check.pkg, name, nil) obj := newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
lhsVars[i] = obj lhsVars[i] = obj
if name != "_" { if name != "_" {
newVars = append(newVars, obj) 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 // create dummy variables where the lhs is invalid
for i, obj := range lhsVars { for i, obj := range lhsVars {
if obj == nil { 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 { func makeSig(res Type, args ...Type) *Signature {
list := make([]*Var, len(args)) list := make([]*Var, len(args))
for i, param := range args { for i, param := range args {
list[i] = NewVar(nopos, nil, "", Default(param)) list[i] = NewParam(nopos, nil, "", Default(param))
} }
params := NewTuple(list...) params := NewTuple(list...)
var result *Tuple var result *Tuple
if res != nil { if res != nil {
assert(!isUntyped(res)) assert(!isUntyped(res))
result = NewTuple(NewVar(nopos, nil, "", res)) result = NewTuple(newVar(ResultVar, nopos, nil, "", res))
} }
return &Signature{params: params, results: result} 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) 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 // 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 // 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. // 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 = "_" 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.mode = value
x.typ = &Signature{ x.typ = &Signature{
tparams: sig.tparams, tparams: sig.tparams,

View File

@ -41,7 +41,7 @@ func TestContextHashCollisions(t *testing.T) {
{ {
// type unaryP = func[P any](_ P) // type unaryP = func[P any](_ P)
tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface) 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) 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)) lhs0 := make([]*Var, len(d.spec.Names))
for i, name := range 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 // initialize all variables

View File

@ -59,7 +59,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
typ := (*Checker)(nil).newInterface() typ := (*Checker)(nil).newInterface()
for _, m := range methods { for _, m := range methods {
if sig := m.typ.(*Signature); sig.recv == nil { 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 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) m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.recordDef(name, m) check.recordDef(name, m)

View File

@ -638,7 +638,7 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) { func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...) // makeSig makes the signature func(typ...)
makeSig := func(typ Type) { makeSig := func(typ Type) {
par := NewVar(nopos, nil, "", typ) par := NewParam(nopos, nil, "", typ)
params := NewTuple(par) params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true) 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). // A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct { type Var struct {
object object
kind VarKind
embedded bool // if set, the variable is an embedded struct field, and name is the type name 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 used bool // set if the variable was used
origin *Var // if non-nil, the Var from which this one was instantiated 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. // NewVar returns a new variable.
// The arguments set the attributes found with all Objects. // 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 { 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. // 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 { 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. // NewField returns a new variable representing a struct field.
// For embedded fields, the name is the unqualified type name // For embedded fields, the name is the unqualified type name
// under which the field is accessible. // under which the field is accessible.
func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { 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. // 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 } func (obj *Var) Embedded() bool { return obj.embedded }
// IsField reports whether the variable is a struct field. // 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 // Origin returns the canonical Var for its receiver, i.e. the Var object
// recorded in Info.Defs. // recorded in Info.Defs.
@ -529,7 +582,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
} }
case *Var: case *Var:
if obj.isField { if obj.IsField() {
buf.WriteString("field") buf.WriteString("field")
} else { } else {
buf.WriteString("var") 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 assert(tv.Type != nil) // should have been recorded already
pos := x.Pos() pos := x.Pos()
tv.Type = NewTuple( tv.Type = NewTuple(
NewVar(pos, check.pkg, "", t0), newVar(LocalVar, pos, check.pkg, "", t0),
NewVar(pos, check.pkg, "", t1), newVar(LocalVar, pos, check.pkg, "", t1),
) )
m[x] = tv m[x] = tv
// if x is a parenthesized expression (p.X), update p.X // if x is a parenthesized expression (p.X), update p.X

View File

@ -379,7 +379,7 @@ func (check *Checker) collectObjects() {
// declare all variables // declare all variables
for i, name := range d.spec.Names { 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 lhs[i] = obj
di := d1 di := d1

View File

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

View File

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

View File

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