Example #1
0
// lockPath returns a typePath describing the location of a lock value
// contained in typ. If there is no contained lock, it returns nil.
func lockPath(tpkg *types.Package, typ types.Type) typePath {
	if typ == nil {
		return nil
	}

	// We're only interested in the case in which the underlying
	// type is a struct. (Interfaces and pointers are safe to copy.)
	styp, ok := typ.Underlying().(*types.Struct)
	if !ok {
		return nil
	}

	// We're looking for cases in which a reference to this type
	// can be locked, but a value cannot. This differentiates
	// embedded interfaces from embedded values.
	if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil {
		if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil {
			return []types.Type{typ}
		}
	}

	nfields := styp.NumFields()
	for i := 0; i < nfields; i++ {
		ftyp := styp.Field(i).Type()
		subpath := lockPath(tpkg, ftyp)
		if subpath != nil {
			return append(subpath, typ)
		}
	}

	return nil
}
Example #2
0
// MethodSet returns the method set of type T.  It is thread-safe.
//
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
// Utility functions can thus expose an optional *MethodSetCache
// parameter to clients that care about performance.
//
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
	if cache == nil {
		return types.NewMethodSet(T)
	}
	cache.mu.Lock()
	defer cache.mu.Unlock()

	switch T := T.(type) {
	case *types.Named:
		return cache.lookupNamed(T).value

	case *types.Pointer:
		if N, ok := T.Elem().(*types.Named); ok {
			return cache.lookupNamed(N).pointer
		}
	}

	// all other types
	// (The map uses pointer equivalence, not type identity.)
	mset := cache.others[T]
	if mset == nil {
		mset = types.NewMethodSet(T)
		if cache.others == nil {
			cache.others = make(map[types.Type]*types.MethodSet)
		}
		cache.others[T] = mset
	}
	return mset
}
Example #3
0
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
	if cache.named == nil {
		cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
	}
	// Avoid recomputing mset(*T) for each distinct Pointer
	// instance whose underlying type is a named type.
	msets, ok := cache.named[named]
	if !ok {
		msets.value = types.NewMethodSet(named)
		msets.pointer = types.NewMethodSet(types.NewPointer(named))
		cache.named[named] = msets
	}
	return msets
}
Example #4
0
//!+
func PrintSkeleton(pkg *types.Package, ifacename, concname string) error {
	obj := pkg.Scope().Lookup(ifacename)
	if obj == nil {
		return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename)
	}
	if _, ok := obj.(*types.TypeName); !ok {
		return fmt.Errorf("%v is not a named type", obj)
	}
	iface, ok := obj.Type().Underlying().(*types.Interface)
	if !ok {
		return fmt.Errorf("type %v is a %T, not an interface",
			obj, obj.Type().Underlying())
	}
	// Use first letter of type name as receiver parameter.
	if !isValidIdentifier(concname) {
		return fmt.Errorf("invalid concrete type name: %q", concname)
	}
	r, _ := utf8.DecodeRuneInString(concname)

	fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename)
	fmt.Printf("type %s struct{}\n", concname)
	mset := types.NewMethodSet(iface)
	for i := 0; i < mset.Len(); i++ {
		meth := mset.At(i).Obj()
		sig := types.TypeString(meth.Type(), (*types.Package).Name)
		fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n",
			r, concname, meth.Name(),
			strings.TrimPrefix(sig, "func"))
	}
	return nil
}
Example #5
0
func FindAllExports(pkg *types.Package, fset *token.FileSet) []UnexportCandidate {
	candidates := []UnexportCandidate{}
	for _, name := range pkg.Scope().Names() {
		obj := pkg.Scope().Lookup(name)
		if !obj.Exported() {
			continue
		}
		displayName := obj.Name()
		if _, ok := obj.(*types.Func); ok {
			displayName += "()"
		}
		candidate := UnexportCandidate{obj.Name(), displayName, fset.Position(obj.Pos())}
		candidates = append(candidates, candidate)
		if tn, ok := obj.(*types.TypeName); ok {
			if str, ok := tn.Type().Underlying().(*types.Struct); ok {
				candidates = append(candidates, findStructFields(str, obj.Name(), fset)...)
			}
			ptrType := types.NewPointer(tn.Type())
			methodSet := types.NewMethodSet(ptrType)
			for i := 0; i < methodSet.Len(); i++ {
				methodSel := methodSet.At(i)
				method := methodSel.Obj()
				// skip unexported functions, and functions from embedded fields.
				// The best I can figure out for embedded functions is if the selection index path is longer than 1.
				if !method.Exported() || len(methodSel.Index()) > 1 {
					continue
				}
				candidate := UnexportCandidate{method.Name(), obj.Name() + "." + method.Name() + "()", fset.Position(method.Pos())}
				candidates = append(candidates, candidate)
			}
		}
	}
	return candidates
}
Example #6
0
// combinedMethodSet returns the method set for a named type T
// merged with all the methods of *T that have different names than
// the methods of T.
//
// combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
// but doesn't require a MethodSetCache.
// TODO(gri) If this functionality doesn't change over time, consider
// just calling IntuitiveMethodSet eventually.
func combinedMethodSet(T *types.Named) []*types.Selection {
	// method set for T
	mset := types.NewMethodSet(T)
	var res []*types.Selection
	for i, n := 0, mset.Len(); i < n; i++ {
		res = append(res, mset.At(i))
	}

	// add all *T methods with names different from T methods
	pmset := types.NewMethodSet(types.NewPointer(T))
	for i, n := 0, pmset.Len(); i < n; i++ {
		pm := pmset.At(i)
		if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
			res = append(res, pm)
		}
	}

	return res
}
Example #7
0
func (p *Processor) isInitPresent(t types.Type) bool {
	ms := types.NewMethodSet(types.NewPointer(t))
	for i := 0; i < ms.Len(); i++ {
		m := ms.At(i)
		if m.Obj().Name() == "Init" {
			return true
		}
	}

	return false
}
Example #8
0
func MustGetMethodListFromGoTypes(typ types.Type) (output []*types.Selection) {
	methodSet := types.NewMethodSet(typ)
	num := methodSet.Len()
	if num == 0 {
		return nil
	}
	output = make([]*types.Selection, num)
	for i := range output {
		output[i] = methodSet.At(i)
	}
	return output
}
Example #9
0
func exportedMethodSet(T types.Type) []*types.Func {
	var methods []*types.Func
	methodset := types.NewMethodSet(T)
	for i := 0; i < methodset.Len(); i++ {
		obj := methodset.At(i).Obj()
		if !obj.Exported() {
			continue
		}
		switch obj := obj.(type) {
		case *types.Func:
			methods = append(methods, obj)
		default:
			log.Panicf("unexpected methodset obj: %s", obj)
		}
	}
	return methods
}
Example #10
0
func TestRpcDemo(ot *testing.T) {
	importPath := "github.com/bronze1man/kmg/kmgGoSource"
	kmgCmd.MustRun("kmg go install " + importPath)
	typ := MustGetGoTypesFromReflect(reflect.TypeOf(&RpcDemo{}))

	typ1, ok := typ.(*types.Pointer)
	kmgTest.Equal(ok, true)

	typ2, ok := typ1.Elem().(*types.Named)
	kmgTest.Equal(ok, true)
	kmgTest.Equal(typ2.NumMethods(), 1)

	obj3 := typ2.Method(0)
	kmgTest.Equal(obj3.Name(), "PostScoreInt")

	typ4, ok := obj3.Type().(*types.Signature)
	kmgTest.Equal(ok, true)
	kmgTest.Equal(typ4.Params().Len(), 3)
	kmgTest.Equal(typ4.Results().Len(), 2)

	for _, testCase := range []struct {
		Type   types.Type
		Expect string
	}{
		{typ4.Params().At(0).Type(), "string"},
		{typ4.Params().At(1).Type(), "int"},
		{typ4.Params().At(2).Type(), "*RpcDemo"},
		{typ4.Results().At(0).Type(), "string"},
		{typ4.Results().At(1).Type(), "error"},
	} {
		typS, importPathList := MustWriteGoTypes("github.com/bronze1man/kmg/kmgGoSource", testCase.Type)
		kmgTest.Equal(typS, testCase.Expect)
		kmgTest.Equal(len(importPathList), 0)
	}

	typS, importPathList := MustWriteGoTypes("github.com/bronze1man/kmg/kmgTest", typ4.Params().At(2).Type())
	kmgTest.Equal(typS, "*kmgGoSource.RpcDemo")
	kmgTest.Equal(importPathList, []string{"github.com/bronze1man/kmg/kmgGoSource"})

	methodSet := types.NewMethodSet(typ)
	kmgTest.Equal(methodSet.Len(), 1)
	kmgTest.Equal(methodSet.At(0).Obj().Name(), "PostScoreInt")

}
Example #11
0
// Smoke test to ensure that imported methods get the correct package.
func TestCorrectMethodPackage(t *testing.T) {
	// This package does not handle gccgo export data.
	if runtime.Compiler == "gccgo" {
		return
	}

	imports := make(map[string]*types.Package)
	_, err := Import(imports, "net/http")
	if err != nil {
		t.Fatal(err)
	}

	mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
	mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
	sel := mset.Lookup(nil, "Lock")
	lock := sel.Obj().(*types.Func)
	if got, want := lock.Pkg().Path(), "sync"; got != want {
		t.Errorf("got package path %q; want %q", got, want)
	}
}
Example #12
0
func (r *implementsResult) display(printf printfFunc) {
	if isInterface(r.t) {
		if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
			printf(r.pos, "empty interface type %s", r.t)
			return
		}

		printf(r.pos, "interface type %s", r.t)
		// Show concrete types first; use two passes.
		for _, sub := range r.to {
			if !isInterface(sub) {
				printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s",
					typeKind(sub), sub)
			}
		}
		for _, sub := range r.to {
			if isInterface(sub) {
				printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s", typeKind(sub), sub)
			}
		}

		for _, super := range r.from {
			printf(super.(*types.Named).Obj(), "\timplements %s", super)
		}
	} else {
		if r.from != nil {
			printf(r.pos, "%s type %s", typeKind(r.t), r.t)
			for _, super := range r.from {
				printf(super.(*types.Named).Obj(), "\timplements %s", super)
			}
		}
		if r.fromPtr != nil {
			printf(r.pos, "pointer type *%s", r.t)
			for _, psuper := range r.fromPtr {
				printf(psuper.(*types.Named).Obj(), "\timplements %s", psuper)
			}
		} else if r.from == nil {
			printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t)
		}
	}
}
Example #13
0
// Smoke test to ensure that imported methods get the correct package.
func TestCorrectMethodPackage(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
	}

	imports := make(map[string]*types.Package)
	_, err := Import(imports, "net/http")
	if err != nil {
		t.Fatal(err)
	}

	mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
	mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
	sel := mset.Lookup(nil, "Lock")
	lock := sel.Obj().(*types.Func)
	if got, want := lock.Pkg().Path(), "sync"; got != want {
		t.Errorf("got package path %q; want %q", got, want)
	}
}
Example #14
0
func serverMethods(serverPkg, serverType string) ([]method, []string, error) {
	cfg := loader.Config{
		TypeCheckFuncBodies: func(string) bool {
			return false
		},
		ImportPkgs: map[string]bool{
			serverPkg: false, // false means don't load tests.
		},
		ParserMode: parser.ParseComments,
	}
	prog, err := cfg.Load()
	if err != nil {
		return nil, nil, errgo.Notef(err, "cannot load %q", serverPkg)
	}
	pkgInfo := prog.Imported[serverPkg]
	if pkgInfo == nil {
		return nil, nil, errgo.Newf("cannot find %q in imported code", serverPkg)
	}
	pkg := pkgInfo.Pkg
	obj := pkg.Scope().Lookup(serverType)
	if obj == nil {
		return nil, nil, errgo.Newf("type %s not found in %s", serverType, serverPkg)
	}
	objTypeName, ok := obj.(*types.TypeName)
	if !ok {
		return nil, nil, errgo.Newf("%s is not a type", serverType)
	}
	// Use the pointer type to get as many methods as possible.
	ptrObjType := types.NewPointer(objTypeName.Type())

	imports := map[string]string{
		"github.com/juju/httprequest": "httprequest",
	}
	var methods []method
	mset := types.NewMethodSet(ptrObjType)
	for i := 0; i < mset.Len(); i++ {
		sel := mset.At(i)
		if !sel.Obj().Exported() {
			continue
		}
		name := sel.Obj().Name()
		if name == "Close" {
			continue
		}
		ptype, rtype, err := parseMethodType(sel.Type().(*types.Signature))
		if err != nil {
			fmt.Fprintf(os.Stderr, "ignoring method %s: %v\n", name, err)
			continue
		}
		comment := docComment(prog, sel)
		methods = append(methods, method{
			Name:      name,
			Doc:       comment,
			ParamType: typeStr(ptype, imports),
			RespType:  typeStr(rtype, imports),
		})
	}
	var allImports []string
	for path := range imports {
		allImports = append(allImports, path)
	}
	return methods, allImports, nil
}
Example #15
0
// Implements displays the "implements" relation as it pertains to the
// selected type.
// If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
//
func implements(q *Query) error {
	lconf := loader.Config{Build: q.Build}
	allowErrors(&lconf)

	qpkg, err := importQueryPackage(q.Pos, &lconf)
	if err != nil {
		return err
	}

	// Set the packages to search.
	if len(q.Scope) > 0 {
		// Inspect all packages in the analysis scope, if specified.
		if err := setPTAScope(&lconf, q.Scope); err != nil {
			return err
		}
	} else {
		// Otherwise inspect the forward and reverse
		// transitive closure of the selected package.
		// (In theory even this is incomplete.)
		_, rev, _ := importgraph.Build(q.Build)
		for path := range rev.Search(qpkg) {
			lconf.ImportWithTests(path)
		}

		// TODO(adonovan): for completeness, we should also
		// type-check and inspect function bodies in all
		// imported packages.  This would be expensive, but we
		// could optimize by skipping functions that do not
		// contain type declarations.  This would require
		// changing the loader's TypeCheckFuncBodies hook to
		// provide the []*ast.File.
	}

	// Load/parse/type-check the program.
	lprog, err := lconf.Load()
	if err != nil {
		return err
	}
	q.Fset = lprog.Fset

	qpos, err := parseQueryPos(lprog, q.Pos, false)
	if err != nil {
		return err
	}

	// Find the selected type.
	path, action := findInterestingNode(qpos.info, qpos.path)

	var method *types.Func
	var T types.Type // selected type (receiver if method != nil)

	switch action {
	case actionExpr:
		// method?
		if id, ok := path[0].(*ast.Ident); ok {
			if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
				recv := obj.Type().(*types.Signature).Recv()
				if recv == nil {
					return fmt.Errorf("this function is not a method")
				}
				method = obj
				T = recv.Type()
			}
		}
	case actionType:
		T = qpos.info.TypeOf(path[0].(ast.Expr))
	}
	if T == nil {
		return fmt.Errorf("no type or method here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	var allNamed []types.Type
	for _, info := range lprog.AllPackages {
		for _, obj := range info.Defs {
			if obj, ok := obj.(*types.TypeName); ok {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	var msets typeutil.MethodSetCache

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if msets.MethodSet(T).Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if msets.MethodSet(U).Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.Identical(T, U) {
					if types.AssignableTo(U, T) {
						to = append(to, U)
					}
					if types.AssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.AssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if msets.MethodSet(U).Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.AssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test determinism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
	if method != nil {
		for _, t := range to {
			toMethod = append(toMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range from {
			fromMethod = append(fromMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range fromPtr {
			fromPtrMethod = append(fromPtrMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
	}

	q.result = &implementsResult{
		qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
	}
	return nil
}
Example #16
0
// Implements displays the "implements" relation as it pertains to the
// selected type.  If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	// Find the selected type.
	path, action := findInterestingNode(qpos.info, qpos.path)

	var method *types.Func
	var T types.Type // selected type (receiver if method != nil)

	switch action {
	case actionExpr:
		// method?
		if id, ok := path[0].(*ast.Ident); ok {
			if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
				recv := obj.Type().(*types.Signature).Recv()
				if recv == nil {
					return nil, fmt.Errorf("this function is not a method")
				}
				method = obj
				T = recv.Type()
			}
		}
	case actionType:
		T = qpos.info.TypeOf(path[0].(ast.Expr))
	}
	if T == nil {
		return nil, fmt.Errorf("no type or method here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	//
	// TODO(adonovan): include all packages in PTA scope too?
	// i.e. don't reduceScope?
	//
	var allNamed []types.Type
	for _, info := range o.typeInfo {
		for _, obj := range info.Defs {
			if obj, ok := obj.(*types.TypeName); ok {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	var msets types.MethodSetCache

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if msets.MethodSet(T).Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if msets.MethodSet(U).Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.Identical(T, U) {
					if types.AssignableTo(U, T) {
						to = append(to, U)
					}
					if types.AssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.AssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if msets.MethodSet(U).Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.AssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test determinism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
	if method != nil {
		for _, t := range to {
			toMethod = append(toMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range from {
			fromMethod = append(fromMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
		for _, t := range fromPtr {
			fromPtrMethod = append(fromPtrMethod,
				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
		}
	}

	return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, nil
}
Example #17
0
func (r *implementsResult) display(printf printfFunc) {
	relation := "is implemented by"

	meth := func(sel *types.Selection) {
		if sel != nil {
			printf(sel.Obj(), "\t%s method (%s).%s",
				relation, r.qpos.TypeString(sel.Recv()), sel.Obj().Name())
		}
	}

	if isInterface(r.t) {
		if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
			printf(r.pos, "empty interface type %s", r.t)
			return
		}

		if r.method == nil {
			printf(r.pos, "interface type %s", r.t)
		} else {
			printf(r.method, "abstract method %s", r.qpos.ObjectString(r.method))
		}

		// Show concrete types (or methods) first; use two passes.
		for i, sub := range r.to {
			if !isInterface(sub) {
				if r.method == nil {
					printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
						relation, typeKind(sub), sub)
				} else {
					meth(r.toMethod[i])
				}
			}
		}
		for i, sub := range r.to {
			if isInterface(sub) {
				if r.method == nil {
					printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
						relation, typeKind(sub), sub)
				} else {
					meth(r.toMethod[i])
				}
			}
		}

		relation = "implements"
		for i, super := range r.from {
			if r.method == nil {
				printf(super.(*types.Named).Obj(), "\t%s %s", relation, super)
			} else {
				meth(r.fromMethod[i])
			}
		}
	} else {
		relation = "implements"

		if r.from != nil {
			if r.method == nil {
				printf(r.pos, "%s type %s", typeKind(r.t), r.t)
			} else {
				printf(r.method, "concrete method %s",
					r.qpos.ObjectString(r.method))
			}
			for i, super := range r.from {
				if r.method == nil {
					printf(super.(*types.Named).Obj(), "\t%s %s",
						relation, super)
				} else {
					meth(r.fromMethod[i])
				}
			}
		}
		if r.fromPtr != nil {
			if r.method == nil {
				printf(r.pos, "pointer type *%s", r.t)
			} else {
				// TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
				printf(r.method, "concrete method %s",
					r.qpos.ObjectString(r.method))
			}

			for i, psuper := range r.fromPtr {
				if r.method == nil {
					printf(psuper.(*types.Named).Obj(), "\t%s %s",
						relation, psuper)
				} else {
					meth(r.fromPtrMethod[i])
				}
			}
		} else if r.from == nil {
			printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t)
		}
	}
}
Example #18
0
// process collects informations about a go package.
func (p *Package) process() error {
	var err error

	funcs := make(map[string]Func)
	structs := make(map[string]Struct)

	scope := p.pkg.Scope()
	for _, name := range scope.Names() {
		obj := scope.Lookup(name)
		if !obj.Exported() {
			continue
		}

		p.n++
		p.syms.addSymbol(obj)
	}

	for _, name := range scope.Names() {
		obj := scope.Lookup(name)
		if !obj.Exported() {
			continue
		}

		switch obj := obj.(type) {
		case *types.Const:
			p.addConst(obj)

		case *types.Var:
			p.addVar(obj)

		case *types.Func:
			funcs[name], err = newFuncFrom(p, "", obj, obj.Type().(*types.Signature))
			if err != nil {
				return err
			}

		case *types.TypeName:
			named := obj.Type().(*types.Named)
			switch typ := named.Underlying().(type) {
			case *types.Struct:
				structs[name], err = newStruct(p, obj)
				if err != nil {
					return err
				}

			case *types.Basic:
				// ok. handled by p.syms-types

			case *types.Array:
				// ok. handled by p.syms-types

			case *types.Interface:
				// ok. handled by p.syms-types

			case *types.Signature:
				// ok. handled by p.syms-types

			case *types.Slice:
				// ok. handled by p.syms-types

			default:
				//TODO(sbinet)
				panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj))
			}

		default:
			//TODO(sbinet)
			panic(fmt.Errorf("not yet supported: %v (%T)", obj, obj))
		}

	}

	// remove ctors from funcs.
	// add methods.
	for sname, s := range structs {
		for name, fct := range funcs {
			if fct.Return() == nil {
				continue
			}
			if fct.Return() == s.GoType() {
				delete(funcs, name)
				fct.doc = p.getDoc(sname, scope.Lookup(name))
				fct.ctor = true
				s.ctors = append(s.ctors, fct)
				structs[sname] = s
			}
		}

		ptyp := types.NewPointer(s.GoType())
		p.syms.addType(nil, ptyp)
		mset := types.NewMethodSet(ptyp)
		for i := 0; i < mset.Len(); i++ {
			meth := mset.At(i)
			if !meth.Obj().Exported() {
				continue
			}
			m, err := newFuncFrom(p, sname, meth.Obj(), meth.Type().(*types.Signature))
			if err != nil {
				return err
			}
			s.meths = append(s.meths, m)
			if isStringer(meth.Obj()) {
				s.prots |= ProtoStringer
			}
		}
		p.addStruct(s)
	}

	for _, fct := range funcs {
		p.addFunc(fct)
	}

	// attach docstrings to methods
	for _, n := range p.syms.names() {
		sym := p.syms.syms[n]
		if !sym.isNamed() {
			continue
		}
		switch typ := sym.GoType().(type) {
		case *types.Named:
			for i := 0; i < typ.NumMethods(); i++ {
				m := typ.Method(i)
				if !m.Exported() {
					continue
				}
				doc := p.getDoc(sym.goname, m)
				mname := types.ObjectString(m, nil)
				msym := p.syms.sym(mname)
				if msym == nil {
					panic(fmt.Errorf(
						"gopy: could not retrieve symbol for %q",
						m.FullName(),
					))
				}
				msym.doc = doc
			}
		}
	}
	return err
}
Example #19
0
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.GenDecl:
		// Variables, constants, types.
		for _, spec := range n.Specs {
			switch spec := spec.(type) {
			case *ast.ValueSpec:
				if constantFlag && n.Tok == token.CONST || variableFlag && n.Tok == token.VAR {
					for _, ident := range spec.Names {
						if f.match(ident.Name) {
							f.printNode(n, ident, f.nameURL(ident.Name))
							break
						}
					}
				}
			case *ast.TypeSpec:
				// If there is only one Spec, there are probably no parens and the
				// comment we want appears before the type keyword, bound to
				// the GenDecl. If the Specs are parenthesized, the comment we want
				// is bound to the Spec. Hence we dig into the GenDecl to the Spec,
				// but only if there are no parens.
				node := ast.Node(n)
				if n.Lparen.IsValid() {
					node = spec
				}
				if f.match(spec.Name.Name) {
					if typeFlag {
						f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
					} else {
						switch spec.Type.(type) {
						case *ast.InterfaceType:
							if interfaceFlag {
								f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
							}
						case *ast.StructType:
							if structFlag {
								f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
							}
						}
					}
					if f.doPrint && f.defs[spec.Name] != nil && f.defs[spec.Name].Type() != nil {
						ms := types.NewMethodSet(f.defs[spec.Name].Type()) //.Type().MethodSet()
						if ms.Len() == 0 {
							ms = types.NewMethodSet(types.NewPointer(f.defs[spec.Name].Type())) //.MethodSet()
						}
						f.methodSet(ms)
					}
				}
			case *ast.ImportSpec:
				continue // Don't care.
			}
		}
	case *ast.FuncDecl:
		// Methods, top-level functions.
		if f.match(n.Name.Name) {
			n.Body = nil // Do not print the function body.
			if methodFlag && n.Recv != nil {
				f.printNode(n, n.Name, f.methodURL(n.Recv.List[0].Type, n.Name.Name))
			} else if functionFlag && n.Recv == nil {
				f.printNode(n, n.Name, f.nameURL(n.Name.Name))
			}
		}
	}
	return f
}