From 31e692be43eea8b6bdaae6d0557f829fe56187be Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 31 Jul 2024 09:40:33 -0700 Subject: [PATCH] go/types, types2: move type recording functionality in its own files This CL simply moves recording functions into recording.go and adjust the imports as needed. There are no other code changes. Preparation for generating go/types/recording.go from types2 sources. Change-Id: Idc5850462a78afb1bfce78ba216722d07c8bca1e Reviewed-on: https://go-review.googlesource.com/c/go/+/602116 LUCI-TryBot-Result: Go LUCI Auto-Submit: Robert Griesemer Reviewed-by: Robert Griesemer Reviewed-by: Tim King --- src/cmd/compile/internal/types2/check.go | 225 ------------------ src/cmd/compile/internal/types2/recording.go | 238 +++++++++++++++++++ src/go/types/check.go | 185 -------------- src/go/types/recording.go | 199 ++++++++++++++++ 4 files changed, 437 insertions(+), 410 deletions(-) create mode 100644 src/cmd/compile/internal/types2/recording.go create mode 100644 src/go/types/recording.go diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 44274b194b..f344142011 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -514,228 +514,3 @@ func (check *Checker) cleanup() { } check.cleaners = nil } - -func (check *Checker) record(x *operand) { - // convert x into a user-friendly set of values - // TODO(gri) this code can be simplified - var typ Type - var val constant.Value - switch x.mode { - case invalid: - typ = Typ[Invalid] - case novalue: - typ = (*Tuple)(nil) - case constant_: - typ = x.typ - val = x.val - default: - typ = x.typ - } - assert(x.expr != nil && typ != nil) - - if isUntyped(typ) { - // delay type and value recording until we know the type - // or until the end of type checking - check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) - } else { - check.recordTypeAndValue(x.expr, x.mode, typ, val) - } -} - -func (check *Checker) recordUntyped() { - if !debug && !check.recordTypes() { - return // nothing to do - } - - for x, info := range check.untyped { - if debug && isTyped(info.typ) { - check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ) - panic("unreachable") - } - check.recordTypeAndValue(x, info.mode, info.typ, info.val) - } -} - -func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) { - assert(x != nil) - assert(typ != nil) - if mode == invalid { - return // omit - } - if mode == constant_ { - assert(val != nil) - // We check allBasic(typ, IsConstType) here as constant expressions may be - // recorded as type parameters. - assert(!isValid(typ) || allBasic(typ, IsConstType)) - } - if m := check.Types; m != nil { - m[x] = TypeAndValue{mode, typ, val} - } - if check.StoreTypesInSyntax { - tv := TypeAndValue{mode, typ, val} - stv := syntax.TypeAndValue{Type: typ, Value: val} - if tv.IsVoid() { - stv.SetIsVoid() - } - if tv.IsType() { - stv.SetIsType() - } - if tv.IsBuiltin() { - stv.SetIsBuiltin() - } - if tv.IsValue() { - stv.SetIsValue() - } - if tv.IsNil() { - stv.SetIsNil() - } - if tv.Addressable() { - stv.SetAddressable() - } - if tv.Assignable() { - stv.SetAssignable() - } - if tv.HasOk() { - stv.SetHasOk() - } - x.SetTypeInfo(stv) - } -} - -func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) { - // f must be a (possibly parenthesized, possibly qualified) - // identifier denoting a built-in (including unsafe's non-constant - // functions Add and Slice): record the signature for f and possible - // children. - for { - check.recordTypeAndValue(f, builtin, sig, nil) - switch p := f.(type) { - case *syntax.Name, *syntax.SelectorExpr: - return // we're done - case *syntax.ParenExpr: - f = p.X - default: - panic("unreachable") - } - } -} - -// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context -// (and therefore has tuple type). -func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) { - assert(x != nil) - assert(len(a) == 2) - if a[0].mode == invalid { - return - } - t0, t1 := a[0].typ, a[1].typ - assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError)) - if m := check.Types; m != nil { - for { - tv := m[x] - 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), - ) - m[x] = tv - // if x is a parenthesized expression (p.X), update p.X - p, _ := x.(*syntax.ParenExpr) - if p == nil { - break - } - x = p.X - } - } - if check.StoreTypesInSyntax { - // Note: this loop is duplicated because the type of tv is different. - // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue. - for { - tv := x.GetTypeInfo() - 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), - ) - x.SetTypeInfo(tv) - p, _ := x.(*syntax.ParenExpr) - if p == nil { - break - } - x = p.X - } - } -} - -// recordInstance records instantiation information into check.Info, if the -// Instances map is non-nil. The given expr must be an ident, selector, or -// index (list) expr with ident or selector operand. -// -// TODO(rfindley): the expr parameter is fragile. See if we can access the -// instantiated identifier in some other way. -func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) { - ident := instantiatedIdent(expr) - assert(ident != nil) - assert(typ != nil) - if m := check.Instances; m != nil { - m[ident] = Instance{newTypeList(targs), typ} - } -} - -func instantiatedIdent(expr syntax.Expr) *syntax.Name { - var selOrIdent syntax.Expr - switch e := expr.(type) { - case *syntax.IndexExpr: - selOrIdent = e.X - case *syntax.SelectorExpr, *syntax.Name: - selOrIdent = e - } - switch x := selOrIdent.(type) { - case *syntax.Name: - return x - case *syntax.SelectorExpr: - return x.Sel - } - panic("instantiated ident not found") -} - -func (check *Checker) recordDef(id *syntax.Name, obj Object) { - assert(id != nil) - if m := check.Defs; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordUse(id *syntax.Name, obj Object) { - assert(id != nil) - assert(obj != nil) - if m := check.Uses; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordImplicit(node syntax.Node, obj Object) { - assert(node != nil) - assert(obj != nil) - if m := check.Implicits; m != nil { - m[node] = obj - } -} - -func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { - assert(obj != nil && (recv == nil || len(index) > 0)) - check.recordUse(x.Sel, obj) - if m := check.Selections; m != nil { - m[x] = &Selection{kind, recv, obj, index, indirect} - } -} - -func (check *Checker) recordScope(node syntax.Node, scope *Scope) { - assert(node != nil) - assert(scope != nil) - if m := check.Scopes; m != nil { - m[node] = scope - } -} diff --git a/src/cmd/compile/internal/types2/recording.go b/src/cmd/compile/internal/types2/recording.go new file mode 100644 index 0000000000..cdd38ddb11 --- /dev/null +++ b/src/cmd/compile/internal/types2/recording.go @@ -0,0 +1,238 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements recording of type information +// in the types2.Info maps. + +package types2 + +import ( + "cmd/compile/internal/syntax" + "go/constant" +) + +func (check *Checker) record(x *operand) { + // convert x into a user-friendly set of values + // TODO(gri) this code can be simplified + var typ Type + var val constant.Value + switch x.mode { + case invalid: + typ = Typ[Invalid] + case novalue: + typ = (*Tuple)(nil) + case constant_: + typ = x.typ + val = x.val + default: + typ = x.typ + } + assert(x.expr != nil && typ != nil) + + if isUntyped(typ) { + // delay type and value recording until we know the type + // or until the end of type checking + check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) + } else { + check.recordTypeAndValue(x.expr, x.mode, typ, val) + } +} + +func (check *Checker) recordUntyped() { + if !debug && !check.recordTypes() { + return // nothing to do + } + + for x, info := range check.untyped { + if debug && isTyped(info.typ) { + check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ) + panic("unreachable") + } + check.recordTypeAndValue(x, info.mode, info.typ, info.val) + } +} + +func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) { + assert(x != nil) + assert(typ != nil) + if mode == invalid { + return // omit + } + if mode == constant_ { + assert(val != nil) + // We check allBasic(typ, IsConstType) here as constant expressions may be + // recorded as type parameters. + assert(!isValid(typ) || allBasic(typ, IsConstType)) + } + if m := check.Types; m != nil { + m[x] = TypeAndValue{mode, typ, val} + } + if check.StoreTypesInSyntax { + tv := TypeAndValue{mode, typ, val} + stv := syntax.TypeAndValue{Type: typ, Value: val} + if tv.IsVoid() { + stv.SetIsVoid() + } + if tv.IsType() { + stv.SetIsType() + } + if tv.IsBuiltin() { + stv.SetIsBuiltin() + } + if tv.IsValue() { + stv.SetIsValue() + } + if tv.IsNil() { + stv.SetIsNil() + } + if tv.Addressable() { + stv.SetAddressable() + } + if tv.Assignable() { + stv.SetAssignable() + } + if tv.HasOk() { + stv.SetHasOk() + } + x.SetTypeInfo(stv) + } +} + +func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) { + // f must be a (possibly parenthesized, possibly qualified) + // identifier denoting a built-in (including unsafe's non-constant + // functions Add and Slice): record the signature for f and possible + // children. + for { + check.recordTypeAndValue(f, builtin, sig, nil) + switch p := f.(type) { + case *syntax.Name, *syntax.SelectorExpr: + return // we're done + case *syntax.ParenExpr: + f = p.X + default: + panic("unreachable") + } + } +} + +// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context +// (and therefore has tuple type). +func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) { + assert(x != nil) + assert(len(a) == 2) + if a[0].mode == invalid { + return + } + t0, t1 := a[0].typ, a[1].typ + assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError)) + if m := check.Types; m != nil { + for { + tv := m[x] + 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), + ) + m[x] = tv + // if x is a parenthesized expression (p.X), update p.X + p, _ := x.(*syntax.ParenExpr) + if p == nil { + break + } + x = p.X + } + } + if check.StoreTypesInSyntax { + // Note: this loop is duplicated because the type of tv is different. + // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue. + for { + tv := x.GetTypeInfo() + 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), + ) + x.SetTypeInfo(tv) + p, _ := x.(*syntax.ParenExpr) + if p == nil { + break + } + x = p.X + } + } +} + +// recordInstance records instantiation information into check.Info, if the +// Instances map is non-nil. The given expr must be an ident, selector, or +// index (list) expr with ident or selector operand. +// +// TODO(rfindley): the expr parameter is fragile. See if we can access the +// instantiated identifier in some other way. +func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) { + ident := instantiatedIdent(expr) + assert(ident != nil) + assert(typ != nil) + if m := check.Instances; m != nil { + m[ident] = Instance{newTypeList(targs), typ} + } +} + +func instantiatedIdent(expr syntax.Expr) *syntax.Name { + var selOrIdent syntax.Expr + switch e := expr.(type) { + case *syntax.IndexExpr: + selOrIdent = e.X + case *syntax.SelectorExpr, *syntax.Name: + selOrIdent = e + } + switch x := selOrIdent.(type) { + case *syntax.Name: + return x + case *syntax.SelectorExpr: + return x.Sel + } + panic("instantiated ident not found") +} + +func (check *Checker) recordDef(id *syntax.Name, obj Object) { + assert(id != nil) + if m := check.Defs; m != nil { + m[id] = obj + } +} + +func (check *Checker) recordUse(id *syntax.Name, obj Object) { + assert(id != nil) + assert(obj != nil) + if m := check.Uses; m != nil { + m[id] = obj + } +} + +func (check *Checker) recordImplicit(node syntax.Node, obj Object) { + assert(node != nil) + assert(obj != nil) + if m := check.Implicits; m != nil { + m[node] = obj + } +} + +func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { + assert(obj != nil && (recv == nil || len(index) > 0)) + check.recordUse(x.Sel, obj) + if m := check.Selections; m != nil { + m[x] = &Selection{kind, recv, obj, index, indirect} + } +} + +func (check *Checker) recordScope(node syntax.Node, scope *Scope) { + assert(node != nil) + assert(scope != nil) + if m := check.Scopes; m != nil { + m[node] = scope + } +} diff --git a/src/go/types/check.go b/src/go/types/check.go index a4d0ff97d5..a31e049c71 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -13,7 +13,6 @@ import ( "go/token" "internal/godebug" . "internal/types/errors" - "strings" "sync/atomic" ) @@ -536,187 +535,3 @@ func (check *Checker) cleanup() { } check.cleaners = nil } - -func (check *Checker) record(x *operand) { - // convert x into a user-friendly set of values - // TODO(gri) this code can be simplified - var typ Type - var val constant.Value - switch x.mode { - case invalid: - typ = Typ[Invalid] - case novalue: - typ = (*Tuple)(nil) - case constant_: - typ = x.typ - val = x.val - default: - typ = x.typ - } - assert(x.expr != nil && typ != nil) - - if isUntyped(typ) { - // delay type and value recording until we know the type - // or until the end of type checking - check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) - } else { - check.recordTypeAndValue(x.expr, x.mode, typ, val) - } -} - -func (check *Checker) recordUntyped() { - if !debug && check.Types == nil { - return // nothing to do - } - - for x, info := range check.untyped { - if debug && isTyped(info.typ) { - check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ) - panic("unreachable") - } - check.recordTypeAndValue(x, info.mode, info.typ, info.val) - } -} - -func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) { - assert(x != nil) - assert(typ != nil) - if mode == invalid { - return // omit - } - if mode == constant_ { - assert(val != nil) - // We check allBasic(typ, IsConstType) here as constant expressions may be - // recorded as type parameters. - assert(!isValid(typ) || allBasic(typ, IsConstType)) - } - if m := check.Types; m != nil { - m[x] = TypeAndValue{mode, typ, val} - } -} - -func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) { - // f must be a (possibly parenthesized, possibly qualified) - // identifier denoting a built-in (including unsafe's non-constant - // functions Add and Slice): record the signature for f and possible - // children. - for { - check.recordTypeAndValue(f, builtin, sig, nil) - switch p := f.(type) { - case *ast.Ident, *ast.SelectorExpr: - return // we're done - case *ast.ParenExpr: - f = p.X - default: - panic("unreachable") - } - } -} - -// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context -// (and therefore has tuple type). -func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) { - assert(x != nil) - assert(len(a) == 2) - if a[0].mode == invalid { - return - } - t0, t1 := a[0].typ, a[1].typ - assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError)) - if m := check.Types; m != nil { - for { - tv := m[x] - 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), - ) - m[x] = tv - // if x is a parenthesized expression (p.X), update p.X - p, _ := x.(*ast.ParenExpr) - if p == nil { - break - } - x = p.X - } - } -} - -// recordInstance records instantiation information into check.Info, if the -// Instances map is non-nil. The given expr must be an ident, selector, or -// index (list) expr with ident or selector operand. -// -// TODO(rfindley): the expr parameter is fragile. See if we can access the -// instantiated identifier in some other way. -func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) { - ident := instantiatedIdent(expr) - assert(ident != nil) - assert(typ != nil) - if m := check.Instances; m != nil { - m[ident] = Instance{newTypeList(targs), typ} - } -} - -func instantiatedIdent(expr ast.Expr) *ast.Ident { - var selOrIdent ast.Expr - switch e := expr.(type) { - case *ast.IndexExpr: - selOrIdent = e.X - case *ast.IndexListExpr: - selOrIdent = e.X - case *ast.SelectorExpr, *ast.Ident: - selOrIdent = e - } - switch x := selOrIdent.(type) { - case *ast.Ident: - return x - case *ast.SelectorExpr: - return x.Sel - } - - // extra debugging of #63933 - var buf strings.Builder - buf.WriteString("instantiated ident not found; please report: ") - ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter) - panic(buf.String()) -} - -func (check *Checker) recordDef(id *ast.Ident, obj Object) { - assert(id != nil) - if m := check.Defs; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordUse(id *ast.Ident, obj Object) { - assert(id != nil) - assert(obj != nil) - if m := check.Uses; m != nil { - m[id] = obj - } -} - -func (check *Checker) recordImplicit(node ast.Node, obj Object) { - assert(node != nil) - assert(obj != nil) - if m := check.Implicits; m != nil { - m[node] = obj - } -} - -func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { - assert(obj != nil && (recv == nil || len(index) > 0)) - check.recordUse(x.Sel, obj) - if m := check.Selections; m != nil { - m[x] = &Selection{kind, recv, obj, index, indirect} - } -} - -func (check *Checker) recordScope(node ast.Node, scope *Scope) { - assert(node != nil) - assert(scope != nil) - if m := check.Scopes; m != nil { - m[node] = scope - } -} diff --git a/src/go/types/recording.go b/src/go/types/recording.go new file mode 100644 index 0000000000..aae2b20d27 --- /dev/null +++ b/src/go/types/recording.go @@ -0,0 +1,199 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements recording of type information +// in the types.Info maps. + +package types + +import ( + "go/ast" + "go/constant" + "go/token" + "strings" +) + +func (check *Checker) record(x *operand) { + // convert x into a user-friendly set of values + // TODO(gri) this code can be simplified + var typ Type + var val constant.Value + switch x.mode { + case invalid: + typ = Typ[Invalid] + case novalue: + typ = (*Tuple)(nil) + case constant_: + typ = x.typ + val = x.val + default: + typ = x.typ + } + assert(x.expr != nil && typ != nil) + + if isUntyped(typ) { + // delay type and value recording until we know the type + // or until the end of type checking + check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val) + } else { + check.recordTypeAndValue(x.expr, x.mode, typ, val) + } +} + +func (check *Checker) recordUntyped() { + if !debug && check.Types == nil { + return // nothing to do + } + + for x, info := range check.untyped { + if debug && isTyped(info.typ) { + check.dump("%v: %s (type %s) is typed", x.Pos(), x, info.typ) + panic("unreachable") + } + check.recordTypeAndValue(x, info.mode, info.typ, info.val) + } +} + +func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) { + assert(x != nil) + assert(typ != nil) + if mode == invalid { + return // omit + } + if mode == constant_ { + assert(val != nil) + // We check allBasic(typ, IsConstType) here as constant expressions may be + // recorded as type parameters. + assert(!isValid(typ) || allBasic(typ, IsConstType)) + } + if m := check.Types; m != nil { + m[x] = TypeAndValue{mode, typ, val} + } +} + +func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) { + // f must be a (possibly parenthesized, possibly qualified) + // identifier denoting a built-in (including unsafe's non-constant + // functions Add and Slice): record the signature for f and possible + // children. + for { + check.recordTypeAndValue(f, builtin, sig, nil) + switch p := f.(type) { + case *ast.Ident, *ast.SelectorExpr: + return // we're done + case *ast.ParenExpr: + f = p.X + default: + panic("unreachable") + } + } +} + +// recordCommaOkTypes updates recorded types to reflect that x is used in a commaOk context +// (and therefore has tuple type). +func (check *Checker) recordCommaOkTypes(x ast.Expr, a []*operand) { + assert(x != nil) + assert(len(a) == 2) + if a[0].mode == invalid { + return + } + t0, t1 := a[0].typ, a[1].typ + assert(isTyped(t0) && isTyped(t1) && (allBoolean(t1) || t1 == universeError)) + if m := check.Types; m != nil { + for { + tv := m[x] + 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), + ) + m[x] = tv + // if x is a parenthesized expression (p.X), update p.X + p, _ := x.(*ast.ParenExpr) + if p == nil { + break + } + x = p.X + } + } +} + +// recordInstance records instantiation information into check.Info, if the +// Instances map is non-nil. The given expr must be an ident, selector, or +// index (list) expr with ident or selector operand. +// +// TODO(rfindley): the expr parameter is fragile. See if we can access the +// instantiated identifier in some other way. +func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) { + ident := instantiatedIdent(expr) + assert(ident != nil) + assert(typ != nil) + if m := check.Instances; m != nil { + m[ident] = Instance{newTypeList(targs), typ} + } +} + +func instantiatedIdent(expr ast.Expr) *ast.Ident { + var selOrIdent ast.Expr + switch e := expr.(type) { + case *ast.IndexExpr: + selOrIdent = e.X + case *ast.IndexListExpr: + selOrIdent = e.X + case *ast.SelectorExpr, *ast.Ident: + selOrIdent = e + } + switch x := selOrIdent.(type) { + case *ast.Ident: + return x + case *ast.SelectorExpr: + return x.Sel + } + + // extra debugging of #63933 + var buf strings.Builder + buf.WriteString("instantiated ident not found; please report: ") + ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter) + panic(buf.String()) +} + +func (check *Checker) recordDef(id *ast.Ident, obj Object) { + assert(id != nil) + if m := check.Defs; m != nil { + m[id] = obj + } +} + +func (check *Checker) recordUse(id *ast.Ident, obj Object) { + assert(id != nil) + assert(obj != nil) + if m := check.Uses; m != nil { + m[id] = obj + } +} + +func (check *Checker) recordImplicit(node ast.Node, obj Object) { + assert(node != nil) + assert(obj != nil) + if m := check.Implicits; m != nil { + m[node] = obj + } +} + +func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { + assert(obj != nil && (recv == nil || len(index) > 0)) + check.recordUse(x.Sel, obj) + if m := check.Selections; m != nil { + m[x] = &Selection{kind, recv, obj, index, indirect} + } +} + +func (check *Checker) recordScope(node ast.Node, scope *Scope) { + assert(node != nil) + assert(scope != nil) + if m := check.Scopes; m != nil { + m[node] = scope + } +} -- 2.48.1