Beispiel #1
0
func (g *goGen) typeString(typ types.Type) string {
	pkg := g.Pkg

	switch t := typ.(type) {
	case *types.Named:
		obj := t.Obj()
		if obj.Pkg() == nil { // e.g. error type is *types.Named.
			return types.TypeString(typ, types.RelativeTo(pkg))
		}
		oPkg := obj.Pkg()
		if !g.validPkg(oPkg) && !isWrapperType(t) {
			g.errorf("type %s is defined in %s, which is not bound", t, oPkg)
			return "TODO"
		}

		switch t.Underlying().(type) {
		case *types.Interface, *types.Struct:
			return fmt.Sprintf("%s%s", g.pkgName(oPkg), types.TypeString(typ, types.RelativeTo(oPkg)))
		default:
			g.errorf("unsupported named type %s / %T", t, t)
		}
	case *types.Pointer:
		switch t := t.Elem().(type) {
		case *types.Named:
			return fmt.Sprintf("*%s", g.typeString(t))
		default:
			g.errorf("not yet supported, pointer type %s / %T", t, t)
		}
	default:
		return types.TypeString(typ, types.RelativeTo(pkg))
	}
	return ""
}
Beispiel #2
0
func (g *goGen) typeString(typ types.Type) string {
	pkg := g.pkg

	switch t := typ.(type) {
	case *types.Named:
		obj := t.Obj()
		if obj.Pkg() == nil { // e.g. error type is *types.Named.
			return types.TypeString(typ, types.RelativeTo(pkg))
		}
		if obj.Pkg() != g.pkg {
			g.errorf("type %s not defined in package %s", t, g.pkg)
		}

		switch t.Underlying().(type) {
		case *types.Interface, *types.Struct:
			return fmt.Sprintf("%s.%s", pkg.Name(), types.TypeString(typ, types.RelativeTo(pkg)))
		default:
			g.errorf("unsupported named type %s / %T", t, t)
		}
	case *types.Pointer:
		switch t := t.Elem().(type) {
		case *types.Named:
			return fmt.Sprintf("*%s", g.typeString(t))
		default:
			g.errorf("not yet supported, pointer type %s / %T", t, t)
		}
	default:
		return types.TypeString(typ, types.RelativeTo(pkg))
	}
	return ""
}
Beispiel #3
0
// writeSignature writes to buf the signature sig in declaration syntax.
func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) {
	buf.WriteString("func ")
	if recv := sig.Recv(); recv != nil {
		buf.WriteString("(")
		if n := params[0].Name(); n != "" {
			buf.WriteString(n)
			buf.WriteString(" ")
		}
		types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
		buf.WriteString(") ")
	}
	buf.WriteString(name)
	types.WriteSignature(buf, sig, types.RelativeTo(from))
}
Beispiel #4
0
func TestImportedTypes(t *testing.T) {
	skipSpecialPlatforms(t)

	// This package only handles gc export data.
	if runtime.Compiler != "gc" {
		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
		return
	}

	for _, test := range importedObjectTests {
		s := strings.Split(test.name, ".")
		if len(s) != 2 {
			t.Fatal("inconsistent test data")
		}
		importPath := s[0]
		objName := s[1]

		pkg, err := Import(imports, importPath)
		if err != nil {
			t.Error(err)
			continue
		}

		obj := pkg.Scope().Lookup(objName)
		if obj == nil {
			t.Errorf("%s: object not found", test.name)
			continue
		}

		got := types.ObjectString(obj, types.RelativeTo(pkg))
		if got != test.want {
			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
		}
	}
}
Beispiel #5
0
func printFields(printf printfFunc, node ast.Node, fields []describeField) {
	if len(fields) > 0 {
		printf(node, "Fields:")
	}

	// Align the names and the types (requires two passes).
	var width int
	var names []string
	for _, f := range fields {
		var buf bytes.Buffer
		for _, fld := range f.implicits {
			buf.WriteString(fld.Obj().Name())
			buf.WriteByte('.')
		}
		buf.WriteString(f.field.Name())
		name := buf.String()
		if n := utf8.RuneCountInString(name); n > width {
			width = n
		}
		names = append(names, name)
	}

	for i, f := range fields {
		// Print the field type relative to the package
		// in which it was defined, not the query package,
		printf(f.field, "\t%*s %s", -width, names[i],
			types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg())))
	}
}
Beispiel #6
0
func formatMember(obj types.Object, maxname int) string {
	qualifier := types.RelativeTo(obj.Pkg())
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
	switch obj := obj.(type) {
	case *types.Const:
		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val().String())

	case *types.Func:
		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))

	case *types.TypeName:
		// Abbreviate long aggregate type names.
		var abbrev string
		switch t := obj.Type().Underlying().(type) {
		case *types.Interface:
			if t.NumMethods() > 1 {
				abbrev = "interface{...}"
			}
		case *types.Struct:
			if t.NumFields() > 1 {
				abbrev = "struct{...}"
			}
		}
		if abbrev == "" {
			fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier))
		} else {
			fmt.Fprintf(&buf, " %s", abbrev)
		}

	case *types.Var:
		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
	}
	return buf.String()
}
Beispiel #7
0
func printMethods(printf printfFunc, node ast.Node, methods []*types.Selection) {
	if len(methods) > 0 {
		printf(node, "Method set:")
	}
	for _, meth := range methods {
		// Print the method type relative to the package
		// in which it was defined, not the query package,
		printf(meth.Obj(), "\t%s",
			types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg())))
	}
}
Beispiel #8
0
// prettyFunc pretty-prints fn for the user interface.
// TODO(adonovan): return HTML so we have more markup freedom.
func prettyFunc(this *types.Package, fn *ssa.Function) string {
	if fn.Parent() != nil {
		return fmt.Sprintf("%s in %s",
			types.TypeString(fn.Signature, types.RelativeTo(this)),
			prettyFunc(this, fn.Parent()))
	}
	if fn.Synthetic != "" && fn.Name() == "init" {
		// (This is the actual initializer, not a declared 'func init').
		if fn.Pkg.Pkg == this {
			return "package initializer"
		}
		return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
	}
	return fn.RelString(this)
}
Beispiel #9
0
func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
	qualifier := types.RelativeTo(this)
	var jmethods []serial.DescribeMethod
	for _, meth := range methods {
		var ser serial.DescribeMethod
		if meth != nil { // may contain nils when called by implements (on a method)
			ser = serial.DescribeMethod{
				Name: types.SelectionString(meth, qualifier),
				Pos:  fset.Position(meth.Obj().Pos()).String(),
			}
		}
		jmethods = append(jmethods, ser)
	}
	return jmethods
}
Beispiel #10
0
func (r *freevarsResult) PrintPlain(printf printfFunc) {
	if len(r.refs) == 0 {
		printf(r.qpos, "No free identifiers.")
	} else {
		printf(r.qpos, "Free identifiers:")
		qualifier := types.RelativeTo(r.qpos.info.Pkg)
		for _, ref := range r.refs {
			// Avoid printing "type T T".
			var typstr string
			if ref.kind != "type" && ref.kind != "label" {
				typstr = " " + types.TypeString(ref.typ, qualifier)
			}
			printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr)
		}
	}
}
Beispiel #11
0
func TestIntuitiveMethodSet(t *testing.T) {
	const source = `
package P
type A int
func (A) f()
func (*A) g()
`

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "hello.go", source, 0)
	if err != nil {
		t.Fatal(err)
	}

	var conf types.Config
	pkg, err := conf.Check("P", fset, []*ast.File{f}, nil)
	if err != nil {
		t.Fatal(err)
	}
	qual := types.RelativeTo(pkg)

	for _, test := range []struct {
		expr string // type expression
		want string // intuitive method set
	}{
		{"A", "(A).f (*A).g"},
		{"*A", "(*A).f (*A).g"},
		{"error", "(error).Error"},
		{"*error", ""},
		{"struct{A}", "(struct{A}).f (*struct{A}).g"},
		{"*struct{A}", "(*struct{A}).f (*struct{A}).g"},
	} {
		tv, err := types.Eval(fset, pkg, 0, test.expr)
		if err != nil {
			t.Errorf("Eval(%s) failed: %v", test.expr, err)
		}
		var names []string
		for _, m := range typeutil.IntuitiveMethodSet(tv.Type, nil) {
			name := fmt.Sprintf("(%s).%s", types.TypeString(m.Recv(), qual), m.Obj().Name())
			names = append(names, name)
		}
		got := strings.Join(names, " ")
		if got != test.want {
			t.Errorf("IntuitiveMethodSet(%s) = %q, want %q", test.expr, got, test.want)
		}
	}
}
Beispiel #12
0
func (r *describePackageResult) display(printf printfFunc) {
	printf(r.node, "%s", r.description)

	// Compute max width of name "column".
	maxname := 0
	for _, mem := range r.members {
		if l := len(mem.obj.Name()); l > maxname {
			maxname = l
		}
	}

	for _, mem := range r.members {
		printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
		for _, meth := range mem.methods {
			printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg)))
		}
	}
}
Beispiel #13
0
func printResult(res *rta.Result, from *types.Package) string {
	var buf bytes.Buffer

	writeSorted := func(ss []string) {
		sort.Strings(ss)
		for _, s := range ss {
			fmt.Fprintf(&buf, "  %s\n", s)
		}
	}

	buf.WriteString("Dynamic calls\n")
	var edges []string
	callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error {
		if strings.Contains(e.Description(), "dynamic") {
			edges = append(edges, fmt.Sprintf("%s --> %s",
				e.Caller.Func.RelString(from),
				e.Callee.Func.RelString(from)))
		}
		return nil
	})
	writeSorted(edges)

	buf.WriteString("Reachable functions\n")
	var reachable []string
	for f := range res.Reachable {
		reachable = append(reachable, f.RelString(from))
	}
	writeSorted(reachable)

	buf.WriteString("Reflect types\n")
	var rtypes []string
	res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) {
		if value == false { // accessible to reflection
			rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from)))
		}
	})
	writeSorted(rtypes)

	return strings.TrimSpace(buf.String())
}
Beispiel #14
0
// WritePackage writes to buf a human-readable summary of p.
func WritePackage(buf *bytes.Buffer, p *Package) {
	fmt.Fprintf(buf, "%s:\n", p)

	var names []string
	maxname := 0
	for name := range p.Members {
		if l := len(name); l > maxname {
			maxname = l
		}
		names = append(names, name)
	}

	from := p.Pkg
	sort.Strings(names)
	for _, name := range names {
		switch mem := p.Members[name].(type) {
		case *NamedConst:
			fmt.Fprintf(buf, "  const %-*s %s = %s\n",
				maxname, name, mem.Name(), mem.Value.RelString(from))

		case *Function:
			fmt.Fprintf(buf, "  func  %-*s %s\n",
				maxname, name, relType(mem.Type(), from))

		case *Type:
			fmt.Fprintf(buf, "  type  %-*s %s\n",
				maxname, name, relType(mem.Type().Underlying(), from))
			for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
				fmt.Fprintf(buf, "    %s\n", types.SelectionString(meth, types.RelativeTo(from)))
			}

		case *Global:
			fmt.Fprintf(buf, "  var   %-*s %s\n",
				maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
		}
	}

	fmt.Fprintf(buf, "\n")
}
Beispiel #15
0
func TestStdlib(t *testing.T) {
	if runtime.GOOS == "android" {
		t.Skipf("incomplete std lib on %s", runtime.GOOS)
	}
	if testing.Short() {
		t.Skip("skipping in short mode; uses tons of memory (golang.org/issue/14113)")
	}

	runtime.GC()
	t0 := time.Now()
	var memstats runtime.MemStats
	runtime.ReadMemStats(&memstats)
	alloc := memstats.Alloc

	// Load, parse and type-check the program.
	ctxt := build.Default // copy
	ctxt.GOPATH = ""      // disable GOPATH
	conf := loader.Config{Build: &ctxt}
	for _, path := range buildutil.AllPackages(conf.Build) {
		conf.ImportWithTests(path)
	}

	prog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %v", err)
	}

	t1 := time.Now()
	runtime.GC()
	runtime.ReadMemStats(&memstats)

	numPkgs := len(prog.AllPackages)
	if want := 205; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Dump package members.
	if false {
		for pkg := range prog.AllPackages {
			fmt.Printf("Package %s:\n", pkg.Path())
			scope := pkg.Scope()
			qualifier := types.RelativeTo(pkg)
			for _, name := range scope.Names() {
				if ast.IsExported(name) {
					fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier))
				}
			}
			fmt.Println()
		}
	}

	// Check that Test functions for io/ioutil, regexp and
	// compress/bzip2 are all simultaneously present.
	// (The apparent cycle formed when augmenting all three of
	// these packages by their tests was the original motivation
	// for reporting b/7114.)
	//
	// compress/bzip2.TestBitReader in bzip2_test.go    imports io/ioutil
	// io/ioutil.TestTempFile       in tempfile_test.go imports regexp
	// regexp.TestRE2Search         in exec_test.go     imports compress/bzip2
	for _, test := range []struct{ pkg, fn string }{
		{"io/ioutil", "TestTempFile"},
		{"regexp", "TestRE2Search"},
		{"compress/bzip2", "TestBitReader"},
	} {
		info := prog.Imported[test.pkg]
		if info == nil {
			t.Errorf("failed to load package %q", test.pkg)
			continue
		}
		obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func)
		if obj == nil {
			t.Errorf("package %q has no func %q", test.pkg, test.fn)
			continue
		}
	}

	// Dump some statistics.

	// determine line count
	var lineCount int
	prog.Fset.Iterate(func(f *token.File) bool {
		lineCount += f.LineCount()
		return true
	})

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("#Source lines:        ", lineCount)
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("#MB:                  ", int64(memstats.Alloc-alloc)/1000000)
}
Beispiel #16
0
func relType(t types.Type, from *types.Package) string {
	return types.TypeString(t, types.RelativeTo(from))
}
Beispiel #17
0
// SelectionString prints selection sel relative to the query position.
func (qpos *queryPos) selectionString(sel *types.Selection) string {
	return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg))
}
Beispiel #18
0
// ObjectString prints object obj relative to the query position.
func (qpos *queryPos) objectString(obj types.Object) string {
	return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
}
Beispiel #19
0
// TypeString prints type T relative to the query position.
func (qpos *queryPos) typeString(T types.Type) string {
	return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
}
Beispiel #20
0
// ExampleInfo prints various facts recorded by the type checker in a
// types.Info struct: definitions of and references to each named object,
// and the type, value, and mode of every expression in the package.
func ExampleInfo() {
	// Parse a single source file.
	const input = `
package fib

type S string

var a, b, c = len(b), S(c), "hello"

func fib(x int) int {
	if x < 2 {
		return x
	}
	return fib(x-1) - fib(x-2)
}`
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "fib.go", input, 0)
	if err != nil {
		log.Fatal(err)
	}

	// Type-check the package.
	// We create an empty map for each kind of input
	// we're interested in, and Check populates them.
	info := types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}
	var conf types.Config
	pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info)
	if err != nil {
		log.Fatal(err)
	}

	// Print package-level variables in initialization order.
	fmt.Printf("InitOrder: %v\n\n", info.InitOrder)

	// For each named object, print the line and
	// column of its definition and each of its uses.
	fmt.Println("Defs and Uses of each named object:")
	usesByObj := make(map[types.Object][]string)
	for id, obj := range info.Uses {
		posn := fset.Position(id.Pos())
		lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column)
		usesByObj[obj] = append(usesByObj[obj], lineCol)
	}
	var items []string
	for obj, uses := range usesByObj {
		sort.Strings(uses)
		item := fmt.Sprintf("%s:\n  defined at %s\n  used at %s",
			types.ObjectString(obj, types.RelativeTo(pkg)),
			fset.Position(obj.Pos()),
			strings.Join(uses, ", "))
		items = append(items, item)
	}
	sort.Strings(items) // sort by line:col, in effect
	fmt.Println(strings.Join(items, "\n"))
	fmt.Println()

	fmt.Println("Types and Values of each expression:")
	items = nil
	for expr, tv := range info.Types {
		var buf bytes.Buffer
		posn := fset.Position(expr.Pos())
		tvstr := tv.Type.String()
		if tv.Value != nil {
			tvstr += " = " + tv.Value.String()
		}
		// line:col | expr | mode : type = value
		fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
			posn.Line, posn.Column, exprString(fset, expr),
			mode(tv), tvstr)
		items = append(items, buf.String())
	}
	sort.Strings(items)
	fmt.Println(strings.Join(items, "\n"))

	// Output:
	// InitOrder: [c = "hello" b = S(c) a = len(b)]
	//
	// Defs and Uses of each named object:
	// builtin len:
	//   defined at -
	//   used at 6:15
	// func fib(x int) int:
	//   defined at fib.go:8:6
	//   used at 12:20, 12:9
	// type S string:
	//   defined at fib.go:4:6
	//   used at 6:23
	// type int int:
	//   defined at -
	//   used at 8:12, 8:17
	// type string string:
	//   defined at -
	//   used at 4:8
	// var b S:
	//   defined at fib.go:6:8
	//   used at 6:19
	// var c string:
	//   defined at fib.go:6:11
	//   used at 6:25
	// var x int:
	//   defined at fib.go:8:10
	//   used at 10:10, 12:13, 12:24, 9:5
	//
	// Types and Values of each expression:
	//  4: 8 | string              | type    : string
	//  6:15 | len                 | builtin : func(string) int
	//  6:15 | len(b)              | value   : int
	//  6:19 | b                   | var     : fib.S
	//  6:23 | S                   | type    : fib.S
	//  6:23 | S(c)                | value   : fib.S
	//  6:25 | c                   | var     : string
	//  6:29 | "hello"             | value   : string = "hello"
	//  8:12 | int                 | type    : int
	//  8:17 | int                 | type    : int
	//  9: 5 | x                   | var     : int
	//  9: 5 | x < 2               | value   : untyped bool
	//  9: 9 | 2                   | value   : int = 2
	// 10:10 | x                   | var     : int
	// 12: 9 | fib                 | value   : func(x int) int
	// 12: 9 | fib(x - 1)          | value   : int
	// 12: 9 | fib(x-1) - fib(x-2) | value   : int
	// 12:13 | x                   | var     : int
	// 12:13 | x - 1               | value   : int
	// 12:15 | 1                   | value   : int = 1
	// 12:20 | fib                 | value   : func(x int) int
	// 12:20 | fib(x - 2)          | value   : int
	// 12:24 | x                   | var     : int
	// 12:24 | x - 2               | value   : int
	// 12:26 | 2                   | value   : int = 2
}
Beispiel #21
0
func (sym *symtab) typename(t types.Type, pkg *types.Package) string {
	if pkg == nil {
		return types.TypeString(t, nil)
	}
	return types.TypeString(t, types.RelativeTo(pkg))
}
Beispiel #22
0
func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
	printf(r.obj, "references to %s",
		types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
}
Beispiel #23
0
func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) {
	qualifier := types.RelativeTo(obj.Pkg())
	T := obj.Type().(*types.Named)
	v := &TypeInfoJSON{
		Name:    obj.Name(),
		Size:    sizes.Sizeof(T),
		Align:   sizes.Alignof(T),
		Methods: []anchorJSON{}, // (JS wants non-nil)
	}

	// addFact adds the fact "is implemented by T" (by) or
	// "implements T" (!by) to group.
	addFact := func(group *implGroupJSON, T types.Type, by bool) {
		Tobj := deref(T).(*types.Named).Obj()
		var byKind string
		if by {
			// Show underlying kind of implementing type,
			// e.g. "slice", "array", "struct".
			s := reflect.TypeOf(T.Underlying()).String()
			byKind = strings.ToLower(strings.TrimPrefix(s, "*types."))
		}
		group.Facts = append(group.Facts, implFactJSON{
			ByKind: byKind,
			Other: anchorJSON{
				Href: a.posURL(Tobj.Pos(), len(Tobj.Name())),
				Text: types.TypeString(T, qualifier),
			},
		})
	}

	// IMPLEMENTS
	if r, ok := implements[T]; ok {
		if isInterface(T) {
			// "T is implemented by <conc>" ...
			// "T is implemented by <iface>"...
			// "T implements        <iface>"...
			group := implGroupJSON{
				Descr: types.TypeString(T, qualifier),
			}
			// Show concrete types first; use two passes.
			for _, sub := range r.to {
				if !isInterface(sub) {
					addFact(&group, sub, true)
				}
			}
			for _, sub := range r.to {
				if isInterface(sub) {
					addFact(&group, sub, true)
				}
			}
			for _, super := range r.from {
				addFact(&group, super, false)
			}
			v.ImplGroups = append(v.ImplGroups, group)
		} else {
			// T is concrete.
			if r.from != nil {
				// "T implements <iface>"...
				group := implGroupJSON{
					Descr: types.TypeString(T, qualifier),
				}
				for _, super := range r.from {
					addFact(&group, super, false)
				}
				v.ImplGroups = append(v.ImplGroups, group)
			}
			if r.fromPtr != nil {
				// "*C implements <iface>"...
				group := implGroupJSON{
					Descr: "*" + types.TypeString(T, qualifier),
				}
				for _, psuper := range r.fromPtr {
					addFact(&group, psuper, false)
				}
				v.ImplGroups = append(v.ImplGroups, group)
			}
		}
	}

	// METHOD SETS
	for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) {
		meth := sel.Obj().(*types.Func)
		pos := meth.Pos() // may be 0 for error.Error
		v.Methods = append(v.Methods, anchorJSON{
			Href: a.posURL(pos, len(meth.Name())),
			Text: types.SelectionString(sel, qualifier),
		})
	}

	// Since there can be many specs per decl, we
	// can't attach the link to the keyword 'type'
	// (as we do with 'func'); we use the Ident.
	fi, offset := a.fileAndOffset(obj.Pos())
	fi.addLink(aLink{
		start:   offset,
		end:     offset + len(obj.Name()),
		title:   fmt.Sprintf("type info for %s", obj.Name()),
		onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)),
	})

	// Add info for exported package-level types to the package info.
	if obj.Exported() && isPackageLevel(obj) {
		// TODO(adonovan): Path is not unique!
		// It is possible to declare a non-test package called x_test.
		a.result.pkgInfo(obj.Pkg().Path()).addType(v)
	}
}
Beispiel #24
0
func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) {
	pkg, err := imp(make(map[string]*types.Package), test.pkgpath)
	if err != nil {
		t.Error(err)
		return
	}

	if test.name != "" {
		obj := pkg.Scope().Lookup(test.name)
		if obj == nil {
			t.Errorf("%s: object not found", test.name)
			return
		}

		got := types.ObjectString(obj, types.RelativeTo(pkg))
		if got != test.want {
			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
		}

		if test.wantval != "" {
			gotval := obj.(*types.Const).Val().String()
			if gotval != test.wantval {
				t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval)
			}
		}
	}

	if len(test.wantinits) > 0 {
		initdata := initmap[pkg]
		found := false
		// Check that the package's own init function has the package's priority
		for _, pkginit := range initdata.Inits {
			if pkginit.InitFunc == test.wantinits[0] {
				if initdata.Priority != pkginit.Priority {
					t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority)
				}
				found = true
				break
			}
		}

		if !found {
			t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0])
		}

		// Each init function in the list other than the first one is a
		// dependency of the function immediately before it. Check that
		// the init functions appear in descending priority order.
		priority := initdata.Priority
		for _, wantdepinit := range test.wantinits[1:] {
			found = false
			for _, pkginit := range initdata.Inits {
				if pkginit.InitFunc == wantdepinit {
					if priority <= pkginit.Priority {
						t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority)
					}
					found = true
					priority = pkginit.Priority
					break
				}
			}

			if !found {
				t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit)
			}
		}
	}
}
Beispiel #25
0
func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) {
	// We must not assume the corresponding SSA packages were
	// created (i.e. were transitively error-free).

	// IMPORTS
	for _, f := range info.Files {
		// Package decl.
		fi, offset := a.fileAndOffset(f.Name.Pos())
		fi.addLink(aLink{
			start: offset,
			end:   offset + len(f.Name.Name),
			title: "Package docs for " + info.Pkg.Path(),
			// TODO(adonovan): fix: we're putting the untrusted Path()
			// into a trusted field.  What's the appropriate sanitizer?
			href: "/pkg/" + info.Pkg.Path(),
		})

		// Import specs.
		for _, imp := range f.Imports {
			// Remove quotes.
			L := int(imp.End()-imp.Path.Pos()) - len(`""`)
			path, _ := strconv.Unquote(imp.Path.Value)
			fi, offset := a.fileAndOffset(imp.Path.Pos())
			fi.addLink(aLink{
				start: offset + 1,
				end:   offset + 1 + L,
				title: "Package docs for " + path,
				// TODO(adonovan): fix: we're putting the untrusted path
				// into a trusted field.  What's the appropriate sanitizer?
				href: "/pkg/" + path,
			})
		}
	}

	// RESOLUTION
	qualifier := types.RelativeTo(info.Pkg)
	for id, obj := range info.Uses {
		// Position of the object definition.
		pos := obj.Pos()
		Len := len(obj.Name())

		// Correct the position for non-renaming import specs.
		//  import "sync/atomic"
		//          ^^^^^^^^^^^
		if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() {
			// Assume this is a non-renaming import.
			// NB: not true for degenerate renamings: `import foo "foo"`.
			pos++
			Len = len(obj.Imported().Path())
		}

		if obj.Pkg() == nil {
			continue // don't mark up built-ins.
		}

		fi, offset := a.fileAndOffset(id.NamePos)
		fi.addLink(aLink{
			start: offset,
			end:   offset + len(id.Name),
			title: types.ObjectString(obj, qualifier),
			href:  a.posURL(pos, Len),
		})
	}

	// IMPLEMENTS & METHOD SETS
	for _, obj := range info.Defs {
		if obj, ok := obj.(*types.TypeName); ok {
			a.namedType(obj, implements)
		}
	}
}