mirror of https://github.com/golang/go.git
go/callgraph: gofmt
Gofmt to update doc comments to the new formatting. (There are so many files in x/tools I am breaking up the gofmt'ing into multiple CLs.) For golang/go#51082. Change-Id: I229b90365998244cfa858ae678382f6089b1cdb9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/399360 Run-TryBot: Russ Cox <rsc@golang.org> Auto-Submit: Russ Cox <rsc@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
ce1e683fa6
commit
ad8ef159e2
|
|
@ -3,7 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package callgraph defines the call graph and various algorithms
|
||||
and utilities to operate on it.
|
||||
|
||||
|
|
@ -30,7 +29,6 @@ calling main() and init().
|
|||
Calls to built-in functions (e.g. panic, println) are not represented
|
||||
in the call graph; they are treated like built-in operators of the
|
||||
language.
|
||||
|
||||
*/
|
||||
package callgraph // import "golang.org/x/tools/go/callgraph"
|
||||
|
||||
|
|
@ -51,7 +49,6 @@ import (
|
|||
// A graph may contain nodes that are not reachable from the root.
|
||||
// If the call graph is sound, such nodes indicate unreachable
|
||||
// functions.
|
||||
//
|
||||
type Graph struct {
|
||||
Root *Node // the distinguished root node
|
||||
Nodes map[*ssa.Function]*Node // all nodes by function
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
// Since CHA conservatively assumes that all functions are address-taken
|
||||
// and all concrete types are put into interfaces, it is sound to run on
|
||||
// partial programs, such as libraries without a main or test function.
|
||||
//
|
||||
package cha // import "golang.org/x/tools/go/callgraph/cha"
|
||||
|
||||
import (
|
||||
|
|
@ -34,7 +33,6 @@ import (
|
|||
|
||||
// CallGraph computes the call graph of the specified program using the
|
||||
// Class Hierarchy Analysis algorithm.
|
||||
//
|
||||
func CallGraph(prog *ssa.Program) *callgraph.Graph {
|
||||
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ func expectation(f *ast.File) (string, token.Pos) {
|
|||
// TestCHA runs CHA on each file in inputs, prints the dynamic edges of
|
||||
// the call graph, and compares it with the golden results embedded in
|
||||
// the WANT comment at the end of the file.
|
||||
//
|
||||
func TestCHA(t *testing.T) {
|
||||
for _, filename := range inputs {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
// analysis, but the algorithm is much faster. For example, running the
|
||||
// cmd/callgraph tool on its own source takes ~2.1s for RTA and ~5.4s
|
||||
// for points-to analysis.
|
||||
//
|
||||
package rta // import "golang.org/x/tools/go/callgraph/rta"
|
||||
|
||||
// TODO(adonovan): test it by connecting it to the interpreter and
|
||||
|
|
@ -57,7 +56,6 @@ import (
|
|||
|
||||
// A Result holds the results of Rapid Type Analysis, which includes the
|
||||
// set of reachable functions/methods, runtime types, and the call graph.
|
||||
//
|
||||
type Result struct {
|
||||
// CallGraph is the discovered callgraph.
|
||||
// It does not include edges for calls made via reflection.
|
||||
|
|
@ -262,7 +260,6 @@ func (r *rta) visitFunc(f *ssa.Function) {
|
|||
// If buildCallGraph is true, Result.CallGraph will contain a call
|
||||
// graph; otherwise, only the other fields (reachable functions) are
|
||||
// populated.
|
||||
//
|
||||
func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result {
|
||||
if len(roots) == 0 {
|
||||
return nil
|
||||
|
|
@ -341,7 +338,6 @@ func (r *rta) implementations(I *types.Interface) []types.Type {
|
|||
// addRuntimeType is called for each concrete type that can be the
|
||||
// dynamic type of some interface or reflect.Value.
|
||||
// Adapted from needMethods in go/ssa/builder.go
|
||||
//
|
||||
func (r *rta) addRuntimeType(T types.Type, skip bool) {
|
||||
if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok {
|
||||
if skip && !prev {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ func expectation(f *ast.File) (string, token.Pos) {
|
|||
// The results string consists of two parts: the set of dynamic call
|
||||
// edges, "f --> g", one per line, and the set of reachable functions,
|
||||
// one per line. Each set is sorted.
|
||||
//
|
||||
func TestRTA(t *testing.T) {
|
||||
for _, filename := range inputs {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
// CallGraph computes the call graph of the specified program
|
||||
// considering only static calls.
|
||||
//
|
||||
func CallGraph(prog *ssa.Program) *callgraph.Graph {
|
||||
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import "golang.org/x/tools/go/ssa"
|
|||
|
||||
// CalleesOf returns a new set containing all direct callees of the
|
||||
// caller node.
|
||||
//
|
||||
func CalleesOf(caller *Node) map[*Node]bool {
|
||||
callees := make(map[*Node]bool)
|
||||
for _, e := range caller.Out {
|
||||
|
|
@ -24,7 +23,6 @@ func CalleesOf(caller *Node) map[*Node]bool {
|
|||
// The edge function is called for each edge in postorder. If it
|
||||
// returns non-nil, visitation stops and GraphVisitEdges returns that
|
||||
// value.
|
||||
//
|
||||
func GraphVisitEdges(g *Graph, edge func(*Edge) error) error {
|
||||
seen := make(map[*Node]bool)
|
||||
var visit func(n *Node) error
|
||||
|
|
@ -54,7 +52,6 @@ func GraphVisitEdges(g *Graph, edge func(*Edge) error) error {
|
|||
// ending at some node for which isEnd() returns true. On success,
|
||||
// PathSearch returns the path as an ordered list of edges; on
|
||||
// failure, it returns nil.
|
||||
//
|
||||
func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge {
|
||||
stack := make([]*Edge, 0, 32)
|
||||
seen := make(map[*Node]bool)
|
||||
|
|
@ -82,7 +79,6 @@ func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge {
|
|||
// synthetic functions (except g.Root and package initializers),
|
||||
// preserving the topology. In effect, calls to synthetic wrappers
|
||||
// are "inlined".
|
||||
//
|
||||
func (g *Graph) DeleteSyntheticNodes() {
|
||||
// Measurements on the standard library and go.tools show that
|
||||
// resulting graph has ~15% fewer nodes and 4-8% fewer edges
|
||||
|
|
|
|||
|
|
@ -175,9 +175,10 @@ func (f function) String() string {
|
|||
// We merge such constructs into a single node for simplicity and without
|
||||
// much precision sacrifice as such variables are rare in practice. Both
|
||||
// a and b would be represented as the same PtrInterface(I) node in:
|
||||
// type I interface
|
||||
// var a ***I
|
||||
// var b **I
|
||||
//
|
||||
// type I interface
|
||||
// var a ***I
|
||||
// var b **I
|
||||
type nestedPtrInterface struct {
|
||||
typ types.Type
|
||||
}
|
||||
|
|
@ -195,8 +196,9 @@ func (l nestedPtrInterface) String() string {
|
|||
// constructs into a single node for simplicity and without much precision
|
||||
// sacrifice as such variables are rare in practice. Both a and b would be
|
||||
// represented as the same PtrFunction(func()) node in:
|
||||
// var a *func()
|
||||
// var b **func()
|
||||
//
|
||||
// var a *func()
|
||||
// var b **func()
|
||||
type nestedPtrFunction struct {
|
||||
typ types.Type
|
||||
}
|
||||
|
|
@ -441,7 +443,9 @@ func (b *builder) send(s *ssa.Send) {
|
|||
}
|
||||
|
||||
// selekt generates flows for select statement
|
||||
// a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...]
|
||||
//
|
||||
// a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...]
|
||||
//
|
||||
// between receiving channel registers c_i and corresponding input register t_i. Further,
|
||||
// flows are generated between o_i and a[2 + i]. Note that a is a tuple register of type
|
||||
// <int, bool, r_1, r_2, ...> where the type of r_i is the element type of channel o_i.
|
||||
|
|
@ -544,8 +548,9 @@ func (b *builder) closure(c *ssa.MakeClosure) {
|
|||
// panic creates a flow from arguments to panic instructions to return
|
||||
// registers of all recover statements in the program. Introduces a
|
||||
// global panic node Panic and
|
||||
// 1) for every panic statement p: add p -> Panic
|
||||
// 2) for every recover statement r: add Panic -> r (handled in call)
|
||||
// 1. for every panic statement p: add p -> Panic
|
||||
// 2. for every recover statement r: add Panic -> r (handled in call)
|
||||
//
|
||||
// TODO(zpavlinovic): improve precision by explicitly modeling how panic
|
||||
// values flow from callees to callers and into deferred recover instructions.
|
||||
func (b *builder) panic(p *ssa.Panic) {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,9 @@ func funcName(f *ssa.Function) string {
|
|||
|
||||
// callGraphStr stringifes `g` into a list of strings where
|
||||
// each entry is of the form
|
||||
// f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ...
|
||||
//
|
||||
// f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ...
|
||||
//
|
||||
// f is a function, cs1, ..., csw are call sites in f, and
|
||||
// f1, f2, ..., fx, fy, ... are the resolved callees.
|
||||
func callGraphStr(g *callgraph.Graph) []string {
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ type key uint64
|
|||
// bitpos is the position of a bit. A position is represented by having a 1
|
||||
// bit in that position.
|
||||
// Examples:
|
||||
// * 0b0010 is the position of the `1` bit in 2.
|
||||
// It is the 3rd most specific bit position in big endian encoding
|
||||
// (0b0 and 0b1 are more specific).
|
||||
// * 0b0100 is the position of the bit that 1 and 5 disagree on.
|
||||
// * 0b0 is a special value indicating that all bit agree.
|
||||
// - 0b0010 is the position of the `1` bit in 2.
|
||||
// It is the 3rd most specific bit position in big endian encoding
|
||||
// (0b0 and 0b1 are more specific).
|
||||
// - 0b0100 is the position of the bit that 1 and 5 disagree on.
|
||||
// - 0b0 is a special value indicating that all bit agree.
|
||||
type bitpos uint64
|
||||
|
||||
// prefixes represent a set of keys that all agree with the
|
||||
|
|
@ -35,7 +35,8 @@ type bitpos uint64
|
|||
// A prefix always mask(p, m) == p.
|
||||
//
|
||||
// A key is its own prefix for the bit position 64,
|
||||
// e.g. seeing a `prefix(key)` is not a problem.
|
||||
// e.g. seeing a `prefix(key)` is not a problem.
|
||||
//
|
||||
// Prefixes should never be turned into keys.
|
||||
type prefix uint64
|
||||
|
||||
|
|
@ -64,8 +65,9 @@ func matchPrefix(k prefix, p prefix, b bitpos) bool {
|
|||
// In big endian encoding, this value is the [64-(m-1)] most significant bits of k
|
||||
// followed by a `0` bit at bitpos m, followed m-1 `1` bits.
|
||||
// Examples:
|
||||
// prefix(0b1011) for a bitpos 0b0100 represents the keys:
|
||||
// 0b1000, 0b1001, 0b1010, 0b1011, 0b1100, 0b1101, 0b1110, 0b1111
|
||||
//
|
||||
// prefix(0b1011) for a bitpos 0b0100 represents the keys:
|
||||
// 0b1000, 0b1001, 0b1010, 0b1011, 0b1100, 0b1101, 0b1110, 0b1111
|
||||
//
|
||||
// This mask function has the property that if matchPrefix(k, p, b), then
|
||||
// k <= p if and only if zeroBit(k, m). This induces binary search tree tries.
|
||||
|
|
@ -85,9 +87,10 @@ func ord(m, n bitpos) bool {
|
|||
// can hold that can also be held by a prefix `q` for some bitpos `n`.
|
||||
//
|
||||
// This is equivalent to:
|
||||
// m ==n && p == q,
|
||||
// higher(m, n) && matchPrefix(q, p, m), or
|
||||
// higher(n, m) && matchPrefix(p, q, n)
|
||||
//
|
||||
// m ==n && p == q,
|
||||
// higher(m, n) && matchPrefix(q, p, m), or
|
||||
// higher(n, m) && matchPrefix(p, q, n)
|
||||
func prefixesOverlap(p prefix, m bitpos, q prefix, n bitpos) bool {
|
||||
fbb := n
|
||||
if ord(m, n) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ package trie
|
|||
// will be stored for the key.
|
||||
//
|
||||
// Collision functions must be idempotent:
|
||||
// collision(x, x) == x for all x.
|
||||
//
|
||||
// collision(x, x) == x for all x.
|
||||
//
|
||||
// Collisions functions may be applied whenever a value is inserted
|
||||
// or two maps are merged, or intersected.
|
||||
type Collision func(lhs interface{}, rhs interface{}) interface{}
|
||||
|
|
@ -72,7 +74,8 @@ func (b *Builder) Empty() Map { return Map{b.Scope(), b.empty} }
|
|||
// in the current scope and handle collisions using the collision function c.
|
||||
//
|
||||
// This is roughly corresponds to updating a map[uint64]interface{} by:
|
||||
// if _, ok := m[k]; ok { m[k] = c(m[k], v} else { m[k] = v}
|
||||
//
|
||||
// if _, ok := m[k]; ok { m[k] = c(m[k], v} else { m[k] = v}
|
||||
//
|
||||
// An insertion or update happened whenever Insert(m, ...) != m .
|
||||
func (b *Builder) InsertWith(c Collision, m Map, k uint64, v interface{}) Map {
|
||||
|
|
@ -85,7 +88,8 @@ func (b *Builder) InsertWith(c Collision, m Map, k uint64, v interface{}) Map {
|
|||
//
|
||||
// If there was a previous value mapped by key, keep the previously mapped value.
|
||||
// This is roughly corresponds to updating a map[uint64]interface{} by:
|
||||
// if _, ok := m[k]; ok { m[k] = val }
|
||||
//
|
||||
// if _, ok := m[k]; ok { m[k] = val }
|
||||
//
|
||||
// This is equivalent to b.Merge(m, b.Create({k: v})).
|
||||
func (b *Builder) Insert(m Map, k uint64, v interface{}) Map {
|
||||
|
|
@ -94,7 +98,8 @@ func (b *Builder) Insert(m Map, k uint64, v interface{}) Map {
|
|||
|
||||
// Updates a (key, value) in the map. This is roughly corresponds to
|
||||
// updating a map[uint64]interface{} by:
|
||||
// m[key] = val
|
||||
//
|
||||
// m[key] = val
|
||||
func (b *Builder) Update(m Map, key uint64, val interface{}) Map {
|
||||
return b.InsertWith(TakeRhs, m, key, val)
|
||||
}
|
||||
|
|
@ -148,14 +153,17 @@ func (b *Builder) Remove(m Map, k uint64) Map {
|
|||
|
||||
// Intersect Maps lhs and rhs and returns a map with all of the keys in
|
||||
// both lhs and rhs and the value comes from lhs, i.e.
|
||||
// {(k, lhs[k]) | k in lhs, k in rhs}.
|
||||
//
|
||||
// {(k, lhs[k]) | k in lhs, k in rhs}.
|
||||
func (b *Builder) Intersect(lhs, rhs Map) Map {
|
||||
return b.IntersectWith(TakeLhs, lhs, rhs)
|
||||
}
|
||||
|
||||
// IntersectWith take lhs and rhs and returns the intersection
|
||||
// with the value coming from the collision function, i.e.
|
||||
// {(k, c(lhs[k], rhs[k]) ) | k in lhs, k in rhs}.
|
||||
//
|
||||
// {(k, c(lhs[k], rhs[k]) ) | k in lhs, k in rhs}.
|
||||
//
|
||||
// The elements of the resulting map are always { <k, c(lhs[k], rhs[k]) > }
|
||||
// for each key k that a key in both lhs and rhs.
|
||||
func (b *Builder) IntersectWith(c Collision, lhs, rhs Map) Map {
|
||||
|
|
@ -261,7 +269,9 @@ func (b *Builder) mkLeaf(k key, v interface{}) *leaf {
|
|||
}
|
||||
|
||||
// mkBranch returns the hash-consed representative of the tuple
|
||||
// (prefix, branch, left, right)
|
||||
//
|
||||
// (prefix, branch, left, right)
|
||||
//
|
||||
// in the current scope.
|
||||
func (b *Builder) mkBranch(p prefix, bp bitpos, left node, right node) *branch {
|
||||
br := &branch{
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@
|
|||
// environment abstract domains in program analysis).
|
||||
//
|
||||
// This implementation closely follows the paper:
|
||||
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
|
||||
// Workshop on ML, September 1998, pp. 77–86.
|
||||
//
|
||||
// C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
|
||||
// Workshop on ML, September 1998, pp. 77–86.
|
||||
//
|
||||
// Each Map is immutable and can be read from concurrently. The map does not
|
||||
// guarantee that the value pointed to by the interface{} value is not updated
|
||||
// concurrently.
|
||||
|
|
@ -36,9 +38,9 @@ import (
|
|||
// Maps are immutable and can be read from concurrently.
|
||||
//
|
||||
// Notes on concurrency:
|
||||
// - A Map value itself is an interface and assignments to a Map value can race.
|
||||
// - Map does not guarantee that the value pointed to by the interface{} value
|
||||
// is not updated concurrently.
|
||||
// - A Map value itself is an interface and assignments to a Map value can race.
|
||||
// - Map does not guarantee that the value pointed to by the interface{} value
|
||||
// is not updated concurrently.
|
||||
type Map struct {
|
||||
s Scope
|
||||
n node
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ func sccEqual(sccs1 []string, sccs2 []string) bool {
|
|||
|
||||
// isRevTopSorted checks if sccs of `g` are sorted in reverse
|
||||
// topological order:
|
||||
// for every edge x -> y in g, nodeToScc[x] > nodeToScc[y]
|
||||
//
|
||||
// for every edge x -> y in g, nodeToScc[x] > nodeToScc[y]
|
||||
func isRevTopSorted(g vtaGraph, nodeToScc map[node]int) bool {
|
||||
for n, succs := range g {
|
||||
for s := range succs {
|
||||
|
|
@ -148,39 +149,39 @@ func setName(f *ssa.Function, name string) {
|
|||
// parentheses contain node types and F nodes stand for function
|
||||
// nodes whose content is function named F:
|
||||
//
|
||||
// no-cycles:
|
||||
// t0 (A) -> t1 (B) -> t2 (C)
|
||||
// no-cycles:
|
||||
// t0 (A) -> t1 (B) -> t2 (C)
|
||||
//
|
||||
// trivial-cycle:
|
||||
// <-------- <--------
|
||||
// | | | |
|
||||
// t0 (A) -> t1 (B) ->
|
||||
// trivial-cycle:
|
||||
// <-------- <--------
|
||||
// | | | |
|
||||
// t0 (A) -> t1 (B) ->
|
||||
//
|
||||
// circle-cycle:
|
||||
// t0 (A) -> t1 (A) -> t2 (B)
|
||||
// | |
|
||||
// <--------------------
|
||||
// circle-cycle:
|
||||
// t0 (A) -> t1 (A) -> t2 (B)
|
||||
// | |
|
||||
// <--------------------
|
||||
//
|
||||
// fully-connected:
|
||||
// t0 (A) <-> t1 (B)
|
||||
// \ /
|
||||
// t2(C)
|
||||
// fully-connected:
|
||||
// t0 (A) <-> t1 (B)
|
||||
// \ /
|
||||
// t2(C)
|
||||
//
|
||||
// subsumed-scc:
|
||||
// t0 (A) -> t1 (B) -> t2(B) -> t3 (A)
|
||||
// | | | |
|
||||
// | <--------- |
|
||||
// <-----------------------------
|
||||
// subsumed-scc:
|
||||
// t0 (A) -> t1 (B) -> t2(B) -> t3 (A)
|
||||
// | | | |
|
||||
// | <--------- |
|
||||
// <-----------------------------
|
||||
//
|
||||
// more-realistic:
|
||||
// <--------
|
||||
// | |
|
||||
// t0 (A) -->
|
||||
// ---------->
|
||||
// | |
|
||||
// t1 (A) -> t2 (B) -> F1 -> F2 -> F3 -> F4
|
||||
// | | | |
|
||||
// <------- <------------
|
||||
// more-realistic:
|
||||
// <--------
|
||||
// | |
|
||||
// t0 (A) -->
|
||||
// ---------->
|
||||
// | |
|
||||
// t1 (A) -> t2 (B) -> F1 -> F2 -> F3 -> F4
|
||||
// | | | |
|
||||
// <------- <------------
|
||||
func testSuite() map[string]vtaGraph {
|
||||
a := newNamedType("A")
|
||||
b := newNamedType("B")
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ func isReferenceNode(n node) bool {
|
|||
|
||||
// hasInFlow checks if a concrete type can flow to node `n`.
|
||||
// Returns yes iff the type of `n` satisfies one the following:
|
||||
// 1) is an interface
|
||||
// 2) is a (nested) pointer to interface (needed for, say,
|
||||
// 1. is an interface
|
||||
// 2. is a (nested) pointer to interface (needed for, say,
|
||||
// slice elements of nested pointers to interface type)
|
||||
// 3) is a function type (needed for higher-order type flow)
|
||||
// 4) is a (nested) pointer to function (needed for, say,
|
||||
// 3. is a function type (needed for higher-order type flow)
|
||||
// 4. is a (nested) pointer to function (needed for, say,
|
||||
// slice elements of nested pointers to function type)
|
||||
// 5) is a global Recover or Panic node
|
||||
// 5. is a global Recover or Panic node
|
||||
func hasInFlow(n node) bool {
|
||||
if _, ok := n.(panicArg); ok {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package vta computes the call graph of a Go program using the Variable
|
||||
// Type Analysis (VTA) algorithm originally described in ``Practical Virtual
|
||||
// Type Analysis (VTA) algorithm originally described in “Practical Virtual
|
||||
// Method Call Resolution for Java," Vijay Sundaresan, Laurie Hendren,
|
||||
// Chrislain Razafimahefa, Raja Vallée-Rai, Patrick Lam, Etienne Gagnon, and
|
||||
// Charles Godin.
|
||||
|
|
@ -18,22 +18,23 @@
|
|||
//
|
||||
// A type propagation is a directed, labeled graph. A node can represent
|
||||
// one of the following:
|
||||
// - A field of a struct type.
|
||||
// - A local (SSA) variable of a method/function.
|
||||
// - All pointers to a non-interface type.
|
||||
// - The return value of a method.
|
||||
// - All elements in an array.
|
||||
// - All elements in a slice.
|
||||
// - All elements in a map.
|
||||
// - All elements in a channel.
|
||||
// - A global variable.
|
||||
// - A field of a struct type.
|
||||
// - A local (SSA) variable of a method/function.
|
||||
// - All pointers to a non-interface type.
|
||||
// - The return value of a method.
|
||||
// - All elements in an array.
|
||||
// - All elements in a slice.
|
||||
// - All elements in a map.
|
||||
// - All elements in a channel.
|
||||
// - A global variable.
|
||||
//
|
||||
// In addition, the implementation used in this package introduces
|
||||
// a few Go specific kinds of nodes:
|
||||
// - (De)references of nested pointers to interfaces are modeled
|
||||
// as a unique nestedPtrInterface node in the type propagation graph.
|
||||
// - Each function literal is represented as a function node whose
|
||||
// internal value is the (SSA) representation of the function. This
|
||||
// is done to precisely infer flow of higher-order functions.
|
||||
// - (De)references of nested pointers to interfaces are modeled
|
||||
// as a unique nestedPtrInterface node in the type propagation graph.
|
||||
// - Each function literal is represented as a function node whose
|
||||
// internal value is the (SSA) representation of the function. This
|
||||
// is done to precisely infer flow of higher-order functions.
|
||||
//
|
||||
// Edges in the graph represent flow of types (and function literals) through
|
||||
// the program. That is, the model 1) typing constraints that are induced by
|
||||
|
|
|
|||
Loading…
Reference in New Issue