mirror of
https://github.com/golang/go
synced 2024-11-11 21:20:21 -07:00
[dev.regabi] go/types: report unused packages in source order
This is a port of CL 287072 to go/types. Change-Id: I08f56995f0323c1f238d1b44703a481d393471d5 Reviewed-on: https://go-review.googlesource.com/c/go/+/289720 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Robert Findley <rfindley@google.com> Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
813958f13c
commit
c48d1503ba
@ -69,6 +69,12 @@ type importKey struct {
|
|||||||
path, dir string
|
path, dir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A dotImportKey describes a dot-imported object in the given scope.
|
||||||
|
type dotImportKey struct {
|
||||||
|
scope *Scope
|
||||||
|
obj Object
|
||||||
|
}
|
||||||
|
|
||||||
// A Checker maintains the state of the type checker.
|
// A Checker maintains the state of the type checker.
|
||||||
// It must be created with NewChecker.
|
// It must be created with NewChecker.
|
||||||
type Checker struct {
|
type Checker struct {
|
||||||
@ -87,7 +93,8 @@ type Checker struct {
|
|||||||
// (initialized by Files, valid only for the duration of check.Files;
|
// (initialized by Files, valid only for the duration of check.Files;
|
||||||
// maps and lists are allocated on demand)
|
// maps and lists are allocated on demand)
|
||||||
files []*ast.File // package files
|
files []*ast.File // package files
|
||||||
unusedDotImports map[*Scope]map[*Package]*ast.ImportSpec // unused dot-imported packages
|
imports []*PkgName // list of imported packages
|
||||||
|
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
|
||||||
|
|
||||||
firstErr error // first error encountered
|
firstErr error // first error encountered
|
||||||
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
||||||
@ -104,22 +111,6 @@ type Checker struct {
|
|||||||
indent int // indentation for tracing
|
indent int // indentation for tracing
|
||||||
}
|
}
|
||||||
|
|
||||||
// addUnusedImport adds the position of a dot-imported package
|
|
||||||
// pkg to the map of dot imports for the given file scope.
|
|
||||||
func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, spec *ast.ImportSpec) {
|
|
||||||
mm := check.unusedDotImports
|
|
||||||
if mm == nil {
|
|
||||||
mm = make(map[*Scope]map[*Package]*ast.ImportSpec)
|
|
||||||
check.unusedDotImports = mm
|
|
||||||
}
|
|
||||||
m := mm[scope]
|
|
||||||
if m == nil {
|
|
||||||
m = make(map[*Package]*ast.ImportSpec)
|
|
||||||
mm[scope] = m
|
|
||||||
}
|
|
||||||
m[pkg] = spec
|
|
||||||
}
|
|
||||||
|
|
||||||
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
|
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
|
||||||
func (check *Checker) addDeclDep(to Object) {
|
func (check *Checker) addDeclDep(to Object) {
|
||||||
from := check.decl
|
from := check.decl
|
||||||
@ -202,7 +193,8 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
|||||||
func (check *Checker) initFiles(files []*ast.File) {
|
func (check *Checker) initFiles(files []*ast.File) {
|
||||||
// start with a clean slate (check.Files may be called multiple times)
|
// start with a clean slate (check.Files may be called multiple times)
|
||||||
check.files = nil
|
check.files = nil
|
||||||
check.unusedDotImports = nil
|
check.imports = nil
|
||||||
|
check.dotImportMap = nil
|
||||||
|
|
||||||
check.firstErr = nil
|
check.firstErr = nil
|
||||||
check.methods = nil
|
check.methods = nil
|
||||||
@ -272,10 +264,16 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
|||||||
if !check.conf.DisableUnusedImportCheck {
|
if !check.conf.DisableUnusedImportCheck {
|
||||||
check.unusedImports()
|
check.unusedImports()
|
||||||
}
|
}
|
||||||
|
// no longer needed - release memory
|
||||||
|
check.imports = nil
|
||||||
|
check.dotImportMap = nil
|
||||||
|
|
||||||
check.recordUntyped()
|
check.recordUntyped()
|
||||||
|
|
||||||
check.pkg.complete = true
|
check.pkg.complete = true
|
||||||
|
|
||||||
|
// TODO(rFindley) There's more memory we should release at this point.
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,21 +275,26 @@ func (check *Checker) collectObjects() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := NewPkgName(d.spec.Pos(), pkg, name, imp)
|
pkgName := NewPkgName(d.spec.Pos(), pkg, name, imp)
|
||||||
if d.spec.Name != nil {
|
if d.spec.Name != nil {
|
||||||
// in a dot-import, the dot represents the package
|
// in a dot-import, the dot represents the package
|
||||||
check.recordDef(d.spec.Name, obj)
|
check.recordDef(d.spec.Name, pkgName)
|
||||||
} else {
|
} else {
|
||||||
check.recordImplicit(d.spec, obj)
|
check.recordImplicit(d.spec, pkgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "C" {
|
if path == "C" {
|
||||||
// match cmd/compile (not prescribed by spec)
|
// match cmd/compile (not prescribed by spec)
|
||||||
obj.used = true
|
pkgName.used = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// add import to file scope
|
// add import to file scope
|
||||||
|
check.imports = append(check.imports, pkgName)
|
||||||
if name == "." {
|
if name == "." {
|
||||||
|
// dot-import
|
||||||
|
if check.dotImportMap == nil {
|
||||||
|
check.dotImportMap = make(map[dotImportKey]*PkgName)
|
||||||
|
}
|
||||||
// merge imported scope with file scope
|
// merge imported scope with file scope
|
||||||
for _, obj := range imp.scope.elems {
|
for _, obj := range imp.scope.elems {
|
||||||
// A package scope may contain non-exported objects,
|
// A package scope may contain non-exported objects,
|
||||||
@ -303,16 +308,15 @@ func (check *Checker) collectObjects() {
|
|||||||
if alt := fileScope.Insert(obj); alt != nil {
|
if alt := fileScope.Insert(obj); alt != nil {
|
||||||
check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
|
check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
|
} else {
|
||||||
|
check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add position to set of dot-import positions for this file
|
|
||||||
// (this is only needed for "imported but not used" errors)
|
|
||||||
check.addUnusedDotImport(fileScope, imp, d.spec)
|
|
||||||
} else {
|
} else {
|
||||||
// declare imported package object in file scope
|
// declare imported package object in file scope
|
||||||
// (no need to provide s.Name since we called check.recordDef earlier)
|
// (no need to provide s.Name since we called check.recordDef earlier)
|
||||||
check.declare(fileScope, nil, obj, token.NoPos)
|
check.declare(fileScope, nil, pkgName, token.NoPos)
|
||||||
}
|
}
|
||||||
case constDecl:
|
case constDecl:
|
||||||
// declare all constants
|
// declare all constants
|
||||||
@ -566,40 +570,31 @@ func (check *Checker) unusedImports() {
|
|||||||
// any of its exported identifiers. To import a package solely for its side-effects
|
// any of its exported identifiers. To import a package solely for its side-effects
|
||||||
// (initialization), use the blank identifier as explicit package name."
|
// (initialization), use the blank identifier as explicit package name."
|
||||||
|
|
||||||
// check use of regular imported packages
|
for _, obj := range check.imports {
|
||||||
for _, scope := range check.pkg.scope.children /* file scopes */ {
|
if !obj.used && obj.name != "_" {
|
||||||
for _, obj := range scope.elems {
|
check.errorUnusedPkg(obj)
|
||||||
if obj, ok := obj.(*PkgName); ok {
|
}
|
||||||
// Unused "blank imports" are automatically ignored
|
}
|
||||||
// since _ identifiers are not entered into scopes.
|
}
|
||||||
if !obj.used {
|
|
||||||
|
func (check *Checker) errorUnusedPkg(obj *PkgName) {
|
||||||
|
// If the package was imported with a name other than the final
|
||||||
|
// import path element, show it explicitly in the error message.
|
||||||
|
// Note that this handles both renamed imports and imports of
|
||||||
|
// packages containing unconventional package declarations.
|
||||||
|
// Note that this uses / always, even on Windows, because Go import
|
||||||
|
// paths always use forward slashes.
|
||||||
path := obj.imported.path
|
path := obj.imported.path
|
||||||
base := pkgName(path)
|
elem := path
|
||||||
if obj.name == base {
|
if i := strings.LastIndex(elem, "/"); i >= 0 {
|
||||||
|
elem = elem[i+1:]
|
||||||
|
}
|
||||||
|
if obj.name == "" || obj.name == "." || obj.name == elem {
|
||||||
check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
|
check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
|
||||||
} else {
|
} else {
|
||||||
check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
|
check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check use of dot-imported packages
|
|
||||||
for _, unusedDotImports := range check.unusedDotImports {
|
|
||||||
for pkg, pos := range unusedDotImports {
|
|
||||||
check.softErrorf(pos, _UnusedImport, "%q imported but not used", pkg.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pkgName returns the package name (last element) of an import path.
|
|
||||||
func pkgName(path string) string {
|
|
||||||
if i := strings.LastIndex(path, "/"); i >= 0 {
|
|
||||||
path = path[i+1:]
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// dir makes a good-faith attempt to return the directory
|
// dir makes a good-faith attempt to return the directory
|
||||||
// portion of path. If path is empty, the result is ".".
|
// portion of path. If path is empty, the result is ".".
|
||||||
|
@ -8,7 +8,7 @@ import "math"
|
|||||||
import m "math"
|
import m "math"
|
||||||
|
|
||||||
import . "testing" // declares T in file scope
|
import . "testing" // declares T in file scope
|
||||||
import . /* ERROR "imported but not used" */ "unsafe"
|
import . /* ERROR .unsafe. imported but not used */ "unsafe"
|
||||||
import . "fmt" // declares Println in file scope
|
import . "fmt" // declares Println in file scope
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package importdecl1
|
package importdecl1
|
||||||
|
|
||||||
import . /* ERROR "imported but not used" */ "unsafe"
|
import . /* ERROR .unsafe. imported but not used */ "unsafe"
|
||||||
|
|
||||||
type B interface {
|
type B interface {
|
||||||
A
|
A
|
||||||
|
@ -51,12 +51,12 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
|
|||||||
}
|
}
|
||||||
assert(typ != nil)
|
assert(typ != nil)
|
||||||
|
|
||||||
// The object may be dot-imported: If so, remove its package from
|
// The object may have been dot-imported.
|
||||||
// the map of unused dot imports for the respective file scope.
|
// If so, mark the respective package as used.
|
||||||
// (This code is only needed for dot-imports. Without them,
|
// (This code is only needed for dot-imports. Without them,
|
||||||
// we only have to mark variables, see *Var case below).
|
// we only have to mark variables, see *Var case below).
|
||||||
if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
|
if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil {
|
||||||
delete(check.unusedDotImports[scope], pkg)
|
pkgName.used = true
|
||||||
}
|
}
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
|
Loading…
Reference in New Issue
Block a user