// dot-imported variables.
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
v = w
- v_used = v.used
+ v_used = check.usedVars[v]
}
}
}
check.expr(nil, &x, lhs)
if v != nil {
- v.used = v_used // restore v.used
+ check.usedVars[v] = v_used // restore v.used
}
if x.mode == invalid || !isValid(x.typ) {
if pname, _ := obj.(*PkgName); pname != nil {
assert(pname.pkg == check.pkg)
check.recordUse(ident, pname)
- pname.used = true
+ check.usedPkgNames[pname] = true
pkg := pname.imported
var exp Object
// dot-imported variables.
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
v = w
- v_used = v.used
+ v_used = check.usedVars[v]
}
}
}
check.exprOrType(&x, n, true)
if v != nil {
- v.used = v_used // restore v.used
+ check.usedVars[v] = v_used // restore v.used
}
case *syntax.ListExpr:
return check.useN(n.ElemList, lhs)
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
+ usedVars map[*Var]bool // set of used variables
+ usedPkgNames map[*PkgName]bool // set of used package names
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
firstErr error // first error encountered
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
return &Checker{
- conf: conf,
- ctxt: conf.Context,
- pkg: pkg,
- Info: info,
- objMap: make(map[Object]*declInfo),
- impMap: make(map[importKey]*Package),
+ conf: conf,
+ ctxt: conf.Context,
+ pkg: pkg,
+ Info: info,
+ objMap: make(map[Object]*declInfo),
+ impMap: make(map[importKey]*Package),
+ usedVars: make(map[*Var]bool),
+ usedPkgNames: make(map[*PkgName]bool),
}
}
// The provided files must all belong to the same package.
func (check *Checker) initFiles(files []*syntax.File) {
// start with a clean slate (check.Files may be called multiple times)
+ // TODO(gri): what determines which fields are zeroed out here, vs at the end
+ // of checkFiles?
check.files = nil
check.imports = nil
check.dotImportMap = nil
check.seenPkgMap = nil
check.brokenAliases = nil
check.unionTypeSets = nil
+ check.usedVars = nil
+ check.usedPkgNames = nil
check.ctxt = nil
+ // TODO(gri): shouldn't the cleanup above occur after the bailout?
// TODO(gri) There's more memory we should release at this point.
}
type PkgName struct {
object
imported *Package
- used bool // set if the package was used
}
// NewPkgName returns a new PkgName object representing an imported package.
// The remaining arguments set the attributes found with all Objects.
func NewPkgName(pos syntax.Pos, pkg *Package, name string, imported *Package) *PkgName {
- return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported, false}
+ return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported}
}
// Imported returns the package that was imported.
// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
object
+ origin *Var // if non-nil, the Var from which this one was instantiated
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
+ isParam bool // var is a param, for backport of 'used' check to go1.24 (go.dev/issue/72826)
}
// NewVar returns a new variable.
// NewParam returns a new variable representing a function parameter.
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 &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, isParam: true}
}
// NewField returns a new variable representing a struct field.
if imp.fake {
// match 1.17 cmd/compile (not prescribed by spec)
- pkgName.used = true
+ check.usedPkgNames[pkgName] = true
}
// add import to file scope
// (initialization), use the blank identifier as explicit package name."
for _, obj := range check.imports {
- if !obj.used && obj.name != "_" {
+ if obj.name != "_" && !check.usedPkgNames[obj] {
check.errorUnusedPkg(obj)
}
}
{term{}, 12, 24},
// Objects
- {PkgName{}, 64, 104},
+ {PkgName{}, 60, 96},
{Const{}, 64, 104},
{TypeName{}, 56, 88},
{Var{}, 64, 104},
var unused []*Var
for name, elem := range scope.elems {
elem = resolve(name, elem)
- if v, _ := elem.(*Var); v != nil && !v.used {
+ if v, _ := elem.(*Var); v != nil && !v.isParam && !check.usedVars[v] {
unused = append(unused, v)
}
}
if lhs != nil {
var used bool
for _, v := range lhsVars {
- if v.used {
+ if check.usedVars[v] {
used = true
}
- v.used = true // avoid usage error when checking entire function
+ check.usedVars[v] = true // avoid usage error when checking entire function
}
if !used {
check.softErrorf(lhs, UnusedVar, "%s declared and not used", lhs.Value)
if typ == nil || typ == Typ[Invalid] {
// typ == Typ[Invalid] can happen if allowVersion fails.
obj.typ = Typ[Invalid]
- obj.used = true // don't complain about unused variable
+ check.usedVars[obj] = true // don't complain about unused variable
continue
}
// avoid "declared but not used" errors
// (don't use Checker.use - we don't want to evaluate too much)
if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg /* see Checker.use1 */ {
- v.used = true
+ check.usedVars[v] = true
}
return
}
// (This code is only needed for dot-imports. Without them,
// we only have to mark variables, see *Var case below).
if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil {
- pkgName.used = true
+ check.usedPkgNames[pkgName] = true
}
switch obj := obj.(type) {
// from other packages to avoid potential race conditions with
// dot-imported variables.
if obj.pkg == check.pkg {
- obj.used = true
+ check.usedVars[obj] = true
}
check.addDeclDep(obj)
if !isValid(typ) {
// dot-imported variables.
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
v = w
- v_used = v.used
+ v_used = check.usedVars[v]
}
}
}
check.expr(nil, &x, lhs)
if v != nil {
- v.used = v_used // restore v.used
+ check.usedVars[v] = v_used // restore v.used
}
if x.mode == invalid || !isValid(x.typ) {
if pname, _ := obj.(*PkgName); pname != nil {
assert(pname.pkg == check.pkg)
check.recordUse(ident, pname)
- pname.used = true
+ check.usedPkgNames[pname] = true
pkg := pname.imported
var exp Object
// dot-imported variables.
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
v = w
- v_used = v.used
+ v_used = check.usedVars[v]
}
}
}
check.exprOrType(&x, n, true)
if v != nil {
- v.used = v_used // restore v.used
+ check.usedVars[v] = v_used // restore v.used
}
default:
check.rawExpr(nil, &x, e, nil, true)
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types
unionTypeSets map[*Union]*_TypeSet // computed type sets for union types
+ usedVars map[*Var]bool // set of used variables
+ usedPkgNames map[*PkgName]bool // set of used package names
mono monoGraph // graph for detecting non-monomorphizable instantiation loops
firstErr error // first error encountered
conf._EnableAlias = gotypesalias.Value() != "0"
return &Checker{
- conf: conf,
- ctxt: conf.Context,
- fset: fset,
- pkg: pkg,
- Info: info,
- objMap: make(map[Object]*declInfo),
- impMap: make(map[importKey]*Package),
+ conf: conf,
+ ctxt: conf.Context,
+ fset: fset,
+ pkg: pkg,
+ Info: info,
+ objMap: make(map[Object]*declInfo),
+ impMap: make(map[importKey]*Package),
+ usedVars: make(map[*Var]bool),
+ usedPkgNames: make(map[*PkgName]bool),
}
}
// The provided files must all belong to the same package.
func (check *Checker) initFiles(files []*ast.File) {
// start with a clean slate (check.Files may be called multiple times)
+ // TODO(gri): what determines which fields are zeroed out here, vs at the end
+ // of checkFiles?
check.files = nil
check.imports = nil
check.dotImportMap = nil
check.seenPkgMap = nil
check.brokenAliases = nil
check.unionTypeSets = nil
+ check.usedVars = nil
+ check.usedPkgNames = nil
check.ctxt = nil
- // TODO(rFindley) There's more memory we should release at this point.
+ // TODO(gri): shouldn't the cleanup above occur after the bailout?
+ // TODO(gri) There's more memory we should release at this point.
}
// processDelayed processes all delayed actions pushed after top.
type PkgName struct {
object
imported *Package
- used bool // set if the package was used
}
// NewPkgName returns a new PkgName object representing an imported package.
// The remaining arguments set the attributes found with all Objects.
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
- return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported, false}
+ return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported}
}
// Imported returns the package that was imported.
// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
object
+ origin *Var // if non-nil, the Var from which this one was instantiated
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
+ isParam bool // var is a param, for backport of 'used' check to go1.24 (go.dev/issue/72826)
}
// NewVar returns a new variable.
// NewParam returns a new variable representing a function parameter.
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 &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, isParam: true}
}
// NewField returns a new variable representing a struct field.
if imp.fake {
// match 1.17 cmd/compile (not prescribed by spec)
- pkgName.used = true
+ check.usedPkgNames[pkgName] = true
}
// add import to file scope
// (initialization), use the blank identifier as explicit package name."
for _, obj := range check.imports {
- if !obj.used && obj.name != "_" {
+ if obj.name != "_" && !check.usedPkgNames[obj] {
check.errorUnusedPkg(obj)
}
}
{term{}, 12, 24},
// Objects
- {PkgName{}, 48, 88},
+ {PkgName{}, 44, 80},
{Const{}, 48, 88},
{TypeName{}, 40, 72},
{Var{}, 48, 88},
var unused []*Var
for name, elem := range scope.elems {
elem = resolve(name, elem)
- if v, _ := elem.(*Var); v != nil && !v.used {
+ if v, _ := elem.(*Var); v != nil && !v.isParam && !check.usedVars[v] {
unused = append(unused, v)
}
}
}
// If lhs exists, we must have at least one lhs variable that was used.
+ // (We can't use check.usage because that only looks at one scope; and
+ // we don't want to use the same variable for all scopes and change the
+ // variable type underfoot.)
if lhs != nil {
var used bool
for _, v := range lhsVars {
- if v.used {
+ if check.usedVars[v] {
used = true
}
- v.used = true // avoid usage error when checking entire function
+ check.usedVars[v] = true // avoid usage error when checking entire function
}
if !used {
check.softErrorf(lhs, UnusedVar, "%s declared and not used", lhs.Name)
if typ == nil || typ == Typ[Invalid] {
// typ == Typ[Invalid] can happen if allowVersion fails.
obj.typ = Typ[Invalid]
- obj.used = true // don't complain about unused variable
+ check.usedVars[obj] = true // don't complain about unused variable
continue
}
// avoid "declared but not used" errors
// (don't use Checker.use - we don't want to evaluate too much)
if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg /* see Checker.use1 */ {
- v.used = true
+ check.usedVars[v] = true
}
return
}
// (This code is only needed for dot-imports. Without them,
// we only have to mark variables, see *Var case below).
if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil {
- pkgName.used = true
+ check.usedPkgNames[pkgName] = true
}
switch obj := obj.(type) {
// from other packages to avoid potential race conditions with
// dot-imported variables.
if obj.pkg == check.pkg {
- obj.used = true
+ check.usedVars[obj] = true
}
check.addDeclDep(obj)
if !isValid(typ) {