2016-02-11 15:57:17 -07:00
|
|
|
// Copyright 2013 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.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
2016-02-14 20:14:31 -07:00
|
|
|
"go/build"
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
"go/parser"
|
2016-02-11 15:57:17 -07:00
|
|
|
"go/token"
|
|
|
|
"go/types"
|
2016-02-14 20:14:31 -07:00
|
|
|
"io"
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
"log"
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
"os"
|
2016-02-11 15:57:17 -07:00
|
|
|
"sort"
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
"strconv"
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
"strings"
|
|
|
|
"sync"
|
2016-02-11 15:57:17 -07:00
|
|
|
|
|
|
|
"golang.org/x/tools/cmd/guru/serial"
|
2016-02-14 20:14:31 -07:00
|
|
|
"golang.org/x/tools/go/buildutil"
|
2016-02-11 20:57:18 -07:00
|
|
|
"golang.org/x/tools/go/loader"
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
"golang.org/x/tools/imports"
|
2016-02-11 15:57:17 -07:00
|
|
|
"golang.org/x/tools/refactor/importgraph"
|
|
|
|
)
|
|
|
|
|
2017-07-09 23:54:58 -06:00
|
|
|
// The referrers function reports all identifiers that resolve to the same object
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// as the queried identifier, within any package in the workspace.
|
2016-02-11 15:57:17 -07:00
|
|
|
func referrers(q *Query) error {
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
fset := token.NewFileSet()
|
|
|
|
lconf := loader.Config{Fset: fset, Build: q.Build}
|
2016-02-11 15:57:17 -07:00
|
|
|
allowErrors(&lconf)
|
|
|
|
|
|
|
|
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-23 10:31:51 -06:00
|
|
|
// Load tests of the query package
|
|
|
|
// even if the query location is not in the tests.
|
|
|
|
for path := range lconf.ImportPkgs {
|
|
|
|
lconf.ImportPkgs[path] = true
|
|
|
|
}
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// Load/parse/type-check the query package.
|
|
|
|
lprog, err := lconf.Load()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
id, _ := qpos.path[0].(*ast.Ident)
|
|
|
|
if id == nil {
|
|
|
|
return fmt.Errorf("no identifier here")
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
obj := qpos.info.ObjectOf(id)
|
|
|
|
if obj == nil {
|
|
|
|
// Happens for y in "switch y := x.(type)",
|
|
|
|
// the package declaration,
|
|
|
|
// and unresolved identifiers.
|
|
|
|
if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
|
|
|
|
return packageReferrers(q, qpos.info.Pkg.Path())
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
return fmt.Errorf("no object for identifier: %T", qpos.path[1])
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// Imported package name?
|
|
|
|
if pkgname, ok := obj.(*types.PkgName); ok {
|
|
|
|
return packageReferrers(q, pkgname.Imported().Path())
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
if obj.Pkg() == nil {
|
|
|
|
return fmt.Errorf("references to predeclared %q are everywhere!", obj.Name())
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2018-04-23 13:57:10 -06:00
|
|
|
q.Output(fset, &referrersInitialResult{
|
|
|
|
qinfo: qpos.info,
|
|
|
|
obj: obj,
|
|
|
|
})
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// For a globally accessible object defined in package P, we
|
|
|
|
// must load packages that depend on P. Specifically, for a
|
|
|
|
// package-level object, we need load only direct importers
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
// of P, but for a field or method, we must load
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// any package that transitively imports P.
|
2018-04-23 13:57:10 -06:00
|
|
|
|
|
|
|
if global, pkglevel := classify(obj); global {
|
|
|
|
if pkglevel {
|
|
|
|
return globalReferrersPkgLevel(q, obj, fset)
|
|
|
|
}
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// We'll use the the object's position to identify it in the larger program.
|
|
|
|
objposn := fset.Position(obj.Pos())
|
|
|
|
defpkg := obj.Pkg().Path() // defining package
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn)
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
return nil // success
|
|
|
|
}
|
|
|
|
|
|
|
|
// classify classifies objects by how far
|
|
|
|
// we have to look to find references to them.
|
|
|
|
func classify(obj types.Object) (global, pkglevel bool) {
|
|
|
|
if obj.Exported() {
|
|
|
|
if obj.Parent() == nil {
|
|
|
|
// selectable object (field or method)
|
|
|
|
return true, false
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
if obj.Parent() == obj.Pkg().Scope() {
|
|
|
|
// lexical object (package-level var/const/func/type)
|
|
|
|
return true, true
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
}
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// object with unexported named or defined in local scope
|
|
|
|
return false, false
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
// packageReferrers reports all references to the specified package
|
|
|
|
// throughout the workspace.
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
func packageReferrers(q *Query, path string) error {
|
|
|
|
// Scan the workspace and build the import graph.
|
|
|
|
// Ignore broken packages.
|
|
|
|
_, rev, _ := importgraph.Build(q.Build)
|
|
|
|
|
|
|
|
// Find the set of packages that directly import the query package.
|
|
|
|
// Only those packages need typechecking of function bodies.
|
|
|
|
users := rev[path]
|
|
|
|
|
|
|
|
// Load the larger program.
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
lconf := loader.Config{
|
|
|
|
Fset: fset,
|
|
|
|
Build: q.Build,
|
|
|
|
TypeCheckFuncBodies: func(p string) bool {
|
|
|
|
return users[strings.TrimSuffix(p, "_test")]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
allowErrors(&lconf)
|
2016-04-01 13:04:45 -06:00
|
|
|
|
|
|
|
// The importgraph doesn't treat external test packages
|
|
|
|
// as separate nodes, so we must use ImportWithTests.
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
for path := range users {
|
|
|
|
lconf.ImportWithTests(path)
|
|
|
|
}
|
2016-04-01 13:04:45 -06:00
|
|
|
|
|
|
|
// Subtle! AfterTypeCheck needs no mutex for qpkg because the
|
|
|
|
// topological import order gives us the necessary happens-before edges.
|
|
|
|
// TODO(adonovan): what about import cycles?
|
|
|
|
var qpkg *types.Package
|
|
|
|
|
|
|
|
// For efficiency, we scan each package for references
|
|
|
|
// just after it has been type-checked. The loader calls
|
|
|
|
// AfterTypeCheck (concurrently), providing us with a stream of
|
|
|
|
// packages.
|
|
|
|
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
|
|
|
|
// AfterTypeCheck may be called twice for the same package due to augmentation.
|
|
|
|
|
|
|
|
if info.Pkg.Path() == path && qpkg == nil {
|
|
|
|
// Found the package of interest.
|
|
|
|
qpkg = info.Pkg
|
|
|
|
fakepkgname := types.NewPkgName(token.NoPos, qpkg, qpkg.Name(), qpkg)
|
|
|
|
q.Output(fset, &referrersInitialResult{
|
|
|
|
qinfo: info,
|
|
|
|
obj: fakepkgname, // bogus
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only inspect packages that directly import the
|
|
|
|
// declaring package (and thus were type-checked).
|
|
|
|
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
|
|
|
|
// Find PkgNames that refer to qpkg.
|
|
|
|
// TODO(adonovan): perhaps more useful would be to show imports
|
|
|
|
// of the package instead of qualified identifiers.
|
|
|
|
var refs []*ast.Ident
|
|
|
|
for id, obj := range info.Uses {
|
|
|
|
if obj, ok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
|
|
|
|
refs = append(refs, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outputUses(q, fset, refs, info.Pkg)
|
|
|
|
}
|
|
|
|
|
|
|
|
clearInfoFields(info) // save memory
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
lconf.Load() // ignore error
|
|
|
|
|
|
|
|
if qpkg == nil {
|
|
|
|
log.Fatalf("query package %q not found during reloading", path)
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
}
|
2016-04-01 13:04:45 -06:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
func usesOf(queryObj types.Object, info *loader.PackageInfo) []*ast.Ident {
|
|
|
|
var refs []*ast.Ident
|
|
|
|
for id, obj := range info.Uses {
|
|
|
|
if sameObj(queryObj, obj) {
|
|
|
|
refs = append(refs, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return refs
|
|
|
|
}
|
|
|
|
|
|
|
|
// outputUses outputs a result describing refs, which appear in the package denoted by info.
|
|
|
|
func outputUses(q *Query, fset *token.FileSet, refs []*ast.Ident, pkg *types.Package) {
|
|
|
|
if len(refs) > 0 {
|
|
|
|
sort.Sort(byNamePos{fset, refs})
|
|
|
|
q.Output(fset, &referrersPackageResult{
|
|
|
|
pkg: pkg,
|
|
|
|
build: q.Build,
|
|
|
|
fset: fset,
|
|
|
|
refs: refs,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// globalReferrers reports references throughout the entire workspace to the
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
// object (a field or method) at the specified source position.
|
|
|
|
// Its defining package is defpkg, and the query package is qpkg.
|
|
|
|
func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position) error {
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// Scan the workspace and build the import graph.
|
|
|
|
// Ignore broken packages.
|
|
|
|
_, rev, _ := importgraph.Build(q.Build)
|
|
|
|
|
|
|
|
// Find the set of packages that depend on defpkg.
|
|
|
|
// Only function bodies in those packages need type-checking.
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
users := rev.Search(defpkg) // transitive importers
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
|
|
|
|
// Prepare to load the larger program.
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
lconf := loader.Config{
|
|
|
|
Fset: fset,
|
|
|
|
Build: q.Build,
|
|
|
|
TypeCheckFuncBodies: func(p string) bool {
|
|
|
|
return users[strings.TrimSuffix(p, "_test")]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
allowErrors(&lconf)
|
|
|
|
|
|
|
|
// The importgraph doesn't treat external test packages
|
|
|
|
// as separate nodes, so we must use ImportWithTests.
|
|
|
|
for path := range users {
|
|
|
|
lconf.ImportWithTests(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The remainder of this function is somewhat tricky because it
|
|
|
|
// operates on the concurrent stream of packages observed by the
|
|
|
|
// loader's AfterTypeCheck hook. Most of guru's helper
|
|
|
|
// functions assume the entire program has already been loaded,
|
|
|
|
// so we can't use them here.
|
|
|
|
// TODO(adonovan): smooth things out once the other changes have landed.
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
// Results are reported concurrently from within the
|
|
|
|
// AfterTypeCheck hook. The program may provide a useful stream
|
|
|
|
// of information even if the user doesn't let the program run
|
|
|
|
// to completion.
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
var (
|
2018-05-02 14:56:06 -06:00
|
|
|
mu sync.Mutex
|
|
|
|
qobj types.Object
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// For efficiency, we scan each package for references
|
|
|
|
// just after it has been type-checked. The loader calls
|
|
|
|
// AfterTypeCheck (concurrently), providing us with a stream of
|
|
|
|
// packages.
|
|
|
|
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
|
2016-04-01 13:04:45 -06:00
|
|
|
// AfterTypeCheck may be called twice for the same package due to augmentation.
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// Only inspect packages that depend on the declaring package
|
|
|
|
// (and thus were type-checked).
|
|
|
|
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
|
|
|
|
// Record the query object and its package when we see it.
|
|
|
|
mu.Lock()
|
|
|
|
if qobj == nil && info.Pkg.Path() == defpkg {
|
|
|
|
// Find the object by its position (slightly ugly).
|
|
|
|
qobj = findObject(fset, &info.Info, objposn)
|
|
|
|
if qobj == nil {
|
|
|
|
// It really ought to be there;
|
|
|
|
// we found it once already.
|
|
|
|
log.Fatalf("object at %s not found in package %s",
|
|
|
|
objposn, defpkg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
obj := qobj
|
|
|
|
mu.Unlock()
|
|
|
|
|
|
|
|
// Look for references to the query object.
|
|
|
|
if obj != nil {
|
2016-04-01 13:04:45 -06:00
|
|
|
outputUses(q, fset, usesOf(obj, info), info.Pkg)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
}
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
clearInfoFields(info) // save memory
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
lconf.Load() // ignore error
|
2016-02-11 15:57:17 -07:00
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
if qobj == nil {
|
|
|
|
log.Fatal("query object not found during reloading")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil // success
|
|
|
|
}
|
|
|
|
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
// globalReferrersPkgLevel reports references throughout the entire workspace to the package-level object obj.
|
|
|
|
// It assumes that the query object itself has already been reported.
|
|
|
|
func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) error {
|
|
|
|
// globalReferrersPkgLevel uses go/ast and friends instead of go/types.
|
|
|
|
// This affords a considerable performance benefit.
|
|
|
|
// It comes at the cost of some code complexity.
|
|
|
|
//
|
|
|
|
// Here's a high level summary.
|
|
|
|
//
|
|
|
|
// The goal is to find references to the query object p.Q.
|
|
|
|
// There are several possible scenarios, each handled differently.
|
|
|
|
//
|
|
|
|
// 1. We are looking in a package other than p, and p is not dot-imported.
|
|
|
|
// This is the simplest case. Q must be referred to as n.Q,
|
|
|
|
// where n is the name under which p is imported.
|
|
|
|
// We look at all imports of p to gather all names under which it is imported.
|
|
|
|
// (In the typical case, it is imported only once, under its default name.)
|
|
|
|
// Then we look at all selector expressions and report any matches.
|
|
|
|
//
|
|
|
|
// 2. We are looking in a package other than p, and p is dot-imported.
|
|
|
|
// In this case, Q will be referred to just as Q.
|
|
|
|
// Furthermore, go/ast's object resolution will not be able to resolve
|
|
|
|
// Q to any other object, unlike any local (file- or function- or block-scoped) object.
|
|
|
|
// So we look at all matching identifiers and report all unresolvable ones.
|
|
|
|
//
|
|
|
|
// 3. We are looking in package p.
|
|
|
|
// (Care must be taken to separate p and p_test (an xtest package),
|
|
|
|
// and make sure that they are treated as separate packages.)
|
|
|
|
// In this case, we give go/ast the entire package for object resolution,
|
|
|
|
// instead of going file by file.
|
|
|
|
// We then iterate over all identifiers that resolve to the query object.
|
|
|
|
// (The query object itself has already been reported, so we don't re-report it.)
|
|
|
|
//
|
|
|
|
// We always skip all files that don't contain the string Q, as they cannot be
|
|
|
|
// relevant to finding references to Q.
|
|
|
|
//
|
|
|
|
// We parse all files leniently. In the presence of parsing errors, results are best-effort.
|
|
|
|
|
|
|
|
// Scan the workspace and build the import graph.
|
|
|
|
// Ignore broken packages.
|
|
|
|
_, rev, _ := importgraph.Build(q.Build)
|
|
|
|
|
|
|
|
// Find the set of packages that directly import defpkg.
|
|
|
|
defpkg := obj.Pkg().Path()
|
|
|
|
defpkg = strings.TrimSuffix(defpkg, "_test") // package x_test actually has package name x
|
|
|
|
defpkg = imports.VendorlessPath(defpkg) // remove vendor goop
|
|
|
|
|
|
|
|
users := rev[defpkg]
|
|
|
|
if len(users) == 0 {
|
|
|
|
users = make(map[string]bool)
|
|
|
|
}
|
|
|
|
// We also need to check defpkg itself, and its xtests.
|
|
|
|
// For the reverse graph packages, we process xtests with the main package.
|
|
|
|
// defpkg gets special handling; we must distinguish between in-package vs out-of-package.
|
|
|
|
// To make the control flow below simpler, add defpkg and defpkg xtest placeholders.
|
|
|
|
// Use "!test" instead of "_test" because "!" is not a valid character in an import path.
|
|
|
|
// (More precisely, it is not guaranteed to be a valid character in an import path,
|
|
|
|
// so it is unlikely that it will be in use. See https://golang.org/ref/spec#Import_declarations.)
|
|
|
|
users[defpkg] = true
|
|
|
|
users[defpkg+"!test"] = true
|
|
|
|
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defname := obj.Pkg().Name() // name of defining package, used for imports using import path only
|
|
|
|
isxtest := strings.HasSuffix(defname, "_test") // indicates whether the query object is defined in an xtest package
|
|
|
|
|
|
|
|
name := obj.Name()
|
|
|
|
namebytes := []byte(name) // byte slice version of query object name, for early filtering
|
|
|
|
objpos := fset.Position(obj.Pos()) // position of query object, used to prevent re-emitting original decl
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
|
|
|
|
var wg sync.WaitGroup
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
|
|
|
|
for u := range users {
|
2018-04-23 12:53:40 -06:00
|
|
|
u := u
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
uIsXTest := strings.HasSuffix(u, "!test") // indicates whether this package is the special defpkg xtest package
|
|
|
|
u = strings.TrimSuffix(u, "!test")
|
|
|
|
|
|
|
|
// Resolve package.
|
|
|
|
sema <- struct{}{} // acquire token
|
|
|
|
pkg, err := q.Build.Import(u, cwd, build.IgnoreVendor)
|
|
|
|
<-sema // release token
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
// If we're not in the query package,
|
|
|
|
// the object is in another package regardless,
|
|
|
|
// so we want to process all files.
|
|
|
|
// If we are in the query package,
|
|
|
|
// we want to only process the files that are
|
|
|
|
// part of that query package;
|
|
|
|
// that set depends on whether the query package itself is an xtest.
|
|
|
|
inQueryPkg := u == defpkg && isxtest == uIsXTest
|
|
|
|
var files []string
|
|
|
|
if !inQueryPkg || !isxtest {
|
|
|
|
files = append(files, pkg.GoFiles...)
|
|
|
|
files = append(files, pkg.TestGoFiles...)
|
|
|
|
files = append(files, pkg.CgoFiles...) // use raw cgo files, as we're only parsing
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
if !inQueryPkg || isxtest {
|
|
|
|
files = append(files, pkg.XTestGoFiles...)
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
if len(files) == 0 {
|
|
|
|
return
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
var deffiles map[string]*ast.File
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
if inQueryPkg {
|
2018-04-23 12:53:40 -06:00
|
|
|
deffiles = make(map[string]*ast.File)
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
|
2018-04-23 13:43:41 -06:00
|
|
|
buf := new(bytes.Buffer) // reusable buffer for reading files
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
for _, file := range files {
|
|
|
|
if !buildutil.IsAbsPath(q.Build, file) {
|
|
|
|
file = buildutil.JoinPath(q.Build, pkg.Dir, file)
|
|
|
|
}
|
2018-04-23 13:43:41 -06:00
|
|
|
buf.Reset()
|
2018-04-23 12:53:40 -06:00
|
|
|
sema <- struct{}{} // acquire token
|
2018-04-23 13:43:41 -06:00
|
|
|
src, err := readFile(q.Build, file, buf)
|
2018-04-23 12:53:40 -06:00
|
|
|
<-sema // release token
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
// Fast path: If the object's name isn't present anywhere in the source, ignore the file.
|
|
|
|
if !bytes.Contains(src, namebytes) {
|
|
|
|
continue
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
if inQueryPkg {
|
|
|
|
// If we're in the query package, we defer final processing until we have
|
|
|
|
// parsed all of the candidate files in the package.
|
|
|
|
// Best effort; allow errors and use what we can from what remains.
|
|
|
|
f, _ := parser.ParseFile(fset, file, src, parser.AllErrors)
|
|
|
|
if f != nil {
|
|
|
|
deffiles[file] = f
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
continue
|
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
|
|
|
|
// We aren't in the query package. Go file by file.
|
|
|
|
|
|
|
|
// Parse out only the imports, to check whether the defining package
|
|
|
|
// was imported, and if so, under what names.
|
|
|
|
// Best effort; allow errors and use what we can from what remains.
|
|
|
|
f, _ := parser.ParseFile(fset, file, src, parser.ImportsOnly|parser.AllErrors)
|
|
|
|
if f == nil {
|
|
|
|
continue
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
// pkgnames is the set of names by which defpkg is imported in this file.
|
|
|
|
// (Multiple imports in the same file are legal but vanishingly rare.)
|
|
|
|
pkgnames := make([]string, 0, 1)
|
|
|
|
var isdotimport bool
|
|
|
|
for _, imp := range f.Imports {
|
|
|
|
path, err := strconv.Unquote(imp.Path.Value)
|
|
|
|
if err != nil || path != defpkg {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case imp.Name == nil:
|
|
|
|
pkgnames = append(pkgnames, defname)
|
|
|
|
case imp.Name.Name == ".":
|
|
|
|
isdotimport = true
|
|
|
|
default:
|
|
|
|
pkgnames = append(pkgnames, imp.Name.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(pkgnames) == 0 && !isdotimport {
|
|
|
|
// Defining package not imported, bail.
|
|
|
|
continue
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
// Re-parse the entire file.
|
|
|
|
// Parse errors are ok; we'll do the best we can with a partial AST, if we have one.
|
|
|
|
f, _ = parser.ParseFile(fset, file, src, parser.AllErrors)
|
|
|
|
if f == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk the AST looking for references.
|
|
|
|
var refs []*ast.Ident
|
|
|
|
ast.Inspect(f, func(n ast.Node) bool {
|
|
|
|
// Check selector expressions.
|
|
|
|
// If the selector matches the target name,
|
|
|
|
// and the expression is one of the names
|
|
|
|
// that the defining package was imported under,
|
|
|
|
// then we have a match.
|
|
|
|
if sel, ok := n.(*ast.SelectorExpr); ok && sel.Sel.Name == name {
|
|
|
|
if id, ok := sel.X.(*ast.Ident); ok {
|
|
|
|
for _, n := range pkgnames {
|
|
|
|
if n == id.Name {
|
|
|
|
refs = append(refs, sel.Sel)
|
|
|
|
// Don't recurse further, to avoid duplicate entries
|
|
|
|
// from the dot import check below.
|
|
|
|
return false
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
// Dot imports are special.
|
|
|
|
// Objects imported from the defining package are placed in the package scope.
|
|
|
|
// go/ast does not resolve them to an object.
|
|
|
|
// At all other scopes (file, local), go/ast can do the resolution.
|
|
|
|
// So we're looking for object-free idents with the right name.
|
|
|
|
// The only other way to get something with the right name at the package scope
|
|
|
|
// is to *be* the defining package. We handle that case separately (inQueryPkg).
|
|
|
|
if isdotimport {
|
|
|
|
if id, ok := n.(*ast.Ident); ok && id.Obj == nil && id.Name == name {
|
|
|
|
refs = append(refs, id)
|
|
|
|
return false
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
return true
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
})
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
// Emit any references we found.
|
|
|
|
if len(refs) > 0 {
|
|
|
|
q.Output(fset, &referrersPackageResult{
|
|
|
|
pkg: types.NewPackage(pkg.ImportPath, pkg.Name),
|
|
|
|
build: q.Build,
|
|
|
|
fset: fset,
|
|
|
|
refs: refs,
|
|
|
|
})
|
|
|
|
}
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
|
|
|
|
// If we're in the query package, we've now collected all the files in the package.
|
|
|
|
// (Or at least the ones that might contain references to the object.)
|
|
|
|
// Find and emit refs.
|
|
|
|
if inQueryPkg {
|
|
|
|
// Bundle the files together into a package.
|
|
|
|
// This does package-level object resolution.
|
|
|
|
qpkg, _ := ast.NewPackage(fset, deffiles, nil, nil)
|
|
|
|
// Look up the query object; we know that it is defined in the package scope.
|
|
|
|
pkgobj := qpkg.Scope.Objects[name]
|
|
|
|
if pkgobj == nil {
|
|
|
|
panic("missing defpkg object for " + defpkg + "." + name)
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
// Find all references to the query object.
|
|
|
|
var refs []*ast.Ident
|
|
|
|
ast.Inspect(qpkg, func(n ast.Node) bool {
|
|
|
|
if id, ok := n.(*ast.Ident); ok {
|
|
|
|
// Check both that this is a reference to the query object
|
|
|
|
// and that it is not the query object itself;
|
|
|
|
// the query object itself was already emitted.
|
|
|
|
if id.Obj == pkgobj && objpos != fset.Position(id.Pos()) {
|
|
|
|
refs = append(refs, id)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
})
|
2018-04-23 12:53:40 -06:00
|
|
|
if len(refs) > 0 {
|
|
|
|
q.Output(fset, &referrersPackageResult{
|
|
|
|
pkg: types.NewPackage(pkg.ImportPath, pkg.Name),
|
|
|
|
build: q.Build,
|
|
|
|
fset: fset,
|
|
|
|
refs: refs,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
deffiles = nil // allow GC
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
2018-04-23 12:53:40 -06:00
|
|
|
}()
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
}
|
|
|
|
|
2018-04-23 12:53:40 -06:00
|
|
|
wg.Wait()
|
|
|
|
|
cmd/guru: optimize global pkglevel referrer queries
Global, package-level queries can be done without typechecking.
This makes it significantly cheaper.
Instead of typechecking, consider all packages that directly
import the query package. In each of those packages, filter out:
* files that don't contain the query name
* files that don't import the query package
Fully parse the remaining files and look for selector expressions
matching the query package and name.
There are a few twists (dot imports, the query package, xtests).
They are described, along with how they are handled,
in the large comment at the beginning of globalReferrersPkgLevel.
On my machine and GOPATH, this reduces typical time
needed to find referrers to encoding/json.MarshalIndent
from (roughly)
real 0m39.946s
user 2m27.844s
sys 0m54.774s
to (roughly)
real 0m5.687s
user 0m15.793s
sys 0m16.001s
The processing of packages could be parallelized;
that is left for a future change, to ease reviewing.
Benefits from that will be limited;
building the reverse import graph accounts for 73%
of the runtime, and that is dominated by syscalls.
Optimization idea from Alan Donovan.
This work supported by Sourcegraph.
Change-Id: Ib19e25fcdcb27673fb03d7300dba2a53198901ad
Reviewed-on: https://go-review.googlesource.com/97800
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-02-28 18:01:09 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
// findObject returns the object defined at the specified position.
|
|
|
|
func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) types.Object {
|
|
|
|
good := func(obj types.Object) bool {
|
|
|
|
if obj == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
posn := fset.Position(obj.Pos())
|
|
|
|
return posn.Filename == objposn.Filename && posn.Offset == objposn.Offset
|
|
|
|
}
|
|
|
|
for _, obj := range info.Defs {
|
|
|
|
if good(obj) {
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, obj := range info.Implicits {
|
|
|
|
if good(obj) {
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// same reports whether x and y are identical, or both are PkgNames
|
|
|
|
// that import the same Package.
|
|
|
|
//
|
|
|
|
func sameObj(x, y types.Object) bool {
|
|
|
|
if x == y {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if x, ok := x.(*types.PkgName); ok {
|
|
|
|
if y, ok := y.(*types.PkgName); ok {
|
|
|
|
return x.Imported() == y.Imported()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
func clearInfoFields(info *loader.PackageInfo) {
|
|
|
|
// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
|
|
|
|
// (Requires go/types change for Go 1.7.)
|
|
|
|
// info.Pkg.Scope().ClearChildren()
|
|
|
|
|
|
|
|
// Discard the file ASTs and their accumulated type
|
|
|
|
// information to save memory.
|
|
|
|
info.Files = nil
|
|
|
|
info.Defs = make(map[*ast.Ident]types.Object)
|
|
|
|
info.Uses = make(map[*ast.Ident]types.Object)
|
|
|
|
info.Implicits = make(map[ast.Node]types.Object)
|
|
|
|
|
|
|
|
// Also, disable future collection of wholly unneeded
|
|
|
|
// type information for the package in case there is
|
|
|
|
// more type-checking to do (augmentation).
|
|
|
|
info.Types = nil
|
|
|
|
info.Scopes = nil
|
|
|
|
info.Selections = nil
|
|
|
|
}
|
|
|
|
|
2016-02-11 15:57:17 -07:00
|
|
|
// -------- utils --------
|
|
|
|
|
|
|
|
// An deterministic ordering for token.Pos that doesn't
|
|
|
|
// depend on the order in which packages were loaded.
|
|
|
|
func lessPos(fset *token.FileSet, x, y token.Pos) bool {
|
|
|
|
fx := fset.File(x)
|
|
|
|
fy := fset.File(y)
|
|
|
|
if fx != fy {
|
|
|
|
return fx.Name() < fy.Name()
|
|
|
|
}
|
|
|
|
return x < y
|
|
|
|
}
|
|
|
|
|
|
|
|
type byNamePos struct {
|
|
|
|
fset *token.FileSet
|
|
|
|
ids []*ast.Ident
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p byNamePos) Len() int { return len(p.ids) }
|
|
|
|
func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
|
|
|
|
func (p byNamePos) Less(i, j int) bool {
|
|
|
|
return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
|
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
// referrersInitialResult is the initial result of a "referrers" query.
|
|
|
|
type referrersInitialResult struct {
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
qinfo *loader.PackageInfo
|
2016-02-11 15:57:17 -07:00
|
|
|
obj types.Object // object it denotes
|
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
|
|
|
|
printf(r.obj, "references to %s",
|
|
|
|
types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *referrersInitialResult) JSON(fset *token.FileSet) []byte {
|
|
|
|
var objpos string
|
|
|
|
if pos := r.obj.Pos(); pos.IsValid() {
|
|
|
|
objpos = fset.Position(pos).String()
|
|
|
|
}
|
|
|
|
return toJSON(&serial.ReferrersInitial{
|
|
|
|
Desc: r.obj.String(),
|
|
|
|
ObjPos: objpos,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// referrersPackageResult is the streaming result for one package of a "referrers" query.
|
|
|
|
type referrersPackageResult struct {
|
|
|
|
pkg *types.Package
|
|
|
|
build *build.Context
|
|
|
|
fset *token.FileSet
|
|
|
|
refs []*ast.Ident // set of all other references to it
|
|
|
|
}
|
2016-02-11 15:57:17 -07:00
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
// forEachRef calls f(id, text) for id in r.refs, in order.
|
|
|
|
// Text is the text of the line on which id appears.
|
|
|
|
func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
|
2016-02-11 15:57:17 -07:00
|
|
|
// Show referring lines, like grep.
|
|
|
|
type fileinfo struct {
|
|
|
|
refs []*ast.Ident
|
|
|
|
linenums []int // line number of refs[i]
|
|
|
|
data chan interface{} // file contents or error
|
|
|
|
}
|
|
|
|
var fileinfos []*fileinfo
|
|
|
|
fileinfosByName := make(map[string]*fileinfo)
|
|
|
|
|
|
|
|
// First pass: start the file reads concurrently.
|
|
|
|
sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
|
|
|
|
for _, ref := range r.refs {
|
cmd/guru: compute referrers as packages are type checked
This CL makes little observable difference to the behavior but paves the
way for streaming 'referrers' and (later) 'implements' queries which
scan the entire workspace, but print each result as soon as it is found.
The go/loader package now exposes a hook, AfterTypeCheck, that lets
clients inspect each package as soon as it is type-checked, and also
modify it, for instance to release unneeded data structures.
A 'referrers' query applied to an exported object must scan the entire
workspace. It uses this hook so to gather uses of the query object in
streaming fashion. However, for now, it still accumulates the results
and prints them all at the end, though I propose to change that in a
follow-up.
Code details:
- The referrers logic had a 2-iteration loop to load first the query
package and then if necessary the enlarged program. The second
iteration has now been unrolled and split into globalReferrers.
- Queries for package names (whether in a package declaration or
a qualified identifier) have been split off into packageReferrers.
It now loads all direct importers of the query package,
which catches some references that were missing before.
(It used to inspect only the forward dependencies of the query
package.)
Also:
- Referrers.Pos (the position of query identifier) was removed from the
JSON output. It's a nuisance to compute now, and it's already
absent from the plain output.
(In a follow-up, I plan to simplify the information content of the
JSON output exactly what is currently printed in the plain output.)
Change-Id: Ia5677636dc7b0fe4461a5d393107665757fb9a97
Reviewed-on: https://go-review.googlesource.com/19794
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
2016-02-21 16:45:02 -07:00
|
|
|
posn := r.fset.Position(ref.Pos())
|
2016-02-11 15:57:17 -07:00
|
|
|
fi := fileinfosByName[posn.Filename]
|
|
|
|
if fi == nil {
|
|
|
|
fi = &fileinfo{data: make(chan interface{})}
|
|
|
|
fileinfosByName[posn.Filename] = fi
|
|
|
|
fileinfos = append(fileinfos, fi)
|
|
|
|
|
|
|
|
// First request for this file:
|
|
|
|
// start asynchronous read.
|
|
|
|
go func() {
|
|
|
|
sema <- struct{}{} // acquire token
|
2018-04-23 13:43:41 -06:00
|
|
|
content, err := readFile(r.build, posn.Filename, nil)
|
2016-02-11 15:57:17 -07:00
|
|
|
<-sema // release token
|
|
|
|
if err != nil {
|
|
|
|
fi.data <- err
|
|
|
|
} else {
|
|
|
|
fi.data <- content
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
fi.refs = append(fi.refs, ref)
|
|
|
|
fi.linenums = append(fi.linenums, posn.Line)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Second pass: print refs in original order.
|
|
|
|
// One line may have several refs at different columns.
|
|
|
|
for _, fi := range fileinfos {
|
|
|
|
v := <-fi.data // wait for I/O completion
|
|
|
|
|
|
|
|
// Print one item for all refs in a file that could not
|
|
|
|
// be loaded (perhaps due to //line directives).
|
|
|
|
if err, ok := v.(error); ok {
|
|
|
|
var suffix string
|
|
|
|
if more := len(fi.refs) - 1; more > 0 {
|
|
|
|
suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
|
|
|
|
}
|
2016-04-01 13:04:45 -06:00
|
|
|
f(fi.refs[0], err.Error()+suffix)
|
2016-02-11 15:57:17 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lines := bytes.Split(v.([]byte), []byte("\n"))
|
|
|
|
for i, ref := range fi.refs {
|
2016-04-01 13:04:45 -06:00
|
|
|
f(ref, string(lines[fi.linenums[i]-1]))
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-14 20:14:31 -07:00
|
|
|
// readFile is like ioutil.ReadFile, but
|
|
|
|
// it goes through the virtualized build.Context.
|
2018-04-23 13:43:41 -06:00
|
|
|
// If non-nil, buf must have been reset.
|
|
|
|
func readFile(ctxt *build.Context, filename string, buf *bytes.Buffer) ([]byte, error) {
|
2016-02-14 20:14:31 -07:00
|
|
|
rc, err := buildutil.OpenFile(ctxt, filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rc.Close()
|
2018-04-23 13:43:41 -06:00
|
|
|
if buf == nil {
|
|
|
|
buf = new(bytes.Buffer)
|
|
|
|
}
|
|
|
|
if _, err := io.Copy(buf, rc); err != nil {
|
2016-02-14 20:14:31 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2016-04-01 13:04:45 -06:00
|
|
|
func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
|
|
|
|
r.foreachRef(func(id *ast.Ident, text string) {
|
|
|
|
printf(id, "%s", text)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
|
|
|
|
refs := serial.ReferrersPackage{Package: r.pkg.Path()}
|
|
|
|
r.foreachRef(func(id *ast.Ident, text string) {
|
|
|
|
refs.Refs = append(refs.Refs, serial.Ref{
|
|
|
|
Pos: fset.Position(id.NamePos).String(),
|
|
|
|
Text: text,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
return toJSON(refs)
|
2016-02-11 15:57:17 -07:00
|
|
|
}
|