Ejemplo n.º 1
0
func WriteInterfaces(dependencies []*types.Package, w io.Writer, merge bool) {
	allTypeNames := []*types.TypeName{types.New("error").(*types.Named).Obj()}
	for _, dep := range dependencies {
		scope := dep.Scope()
		for _, name := range scope.Names() {
			if typeName, isTypeName := scope.Lookup(name).(*types.TypeName); isTypeName {
				allTypeNames = append(allTypeNames, typeName)
			}
		}
	}
	for _, t := range allTypeNames {
		if in, isInterface := t.Type().Underlying().(*types.Interface); isInterface {
			if in.MethodSet().Len() == 0 {
				continue
			}
			implementedBy := make(map[string]bool, 0)
			for _, other := range allTypeNames {
				otherType := other.Type()
				switch otherType.Underlying().(type) {
				case *types.Interface:
					// skip
				case *types.Struct:
					if types.IsAssignableTo(otherType, in) {
						implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s.Go$NonPointer", other.Pkg().Path(), other.Name())] = true
					}
					if types.IsAssignableTo(types.NewPointer(otherType), in) {
						implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true
					}
				default:
					if types.IsAssignableTo(otherType, in) {
						implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true
					}
					if types.IsAssignableTo(types.NewPointer(otherType), in) {
						implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s.Go$Pointer", other.Pkg().Path(), other.Name())] = true
					}
				}
			}
			list := make([]string, 0, len(implementedBy))
			for ref := range implementedBy {
				list = append(list, ref)
			}
			sort.Strings(list)
			var target string
			switch t.Name() {
			case "error":
				target = "Go$error"
			default:
				target = fmt.Sprintf("Go$packages[\"%s\"].%s", t.Pkg().Path(), t.Name())
			}
			if merge {
				for _, entry := range list {
					fmt.Fprintf(w, "if (%s.Go$implementedBy.indexOf(%s) === -1) { %s.Go$implementedBy.push(%s); }\n", target, entry, target, entry)
				}
				continue
			}
			fmt.Fprintf(w, "%s.Go$implementedBy = [%s];\n", target, strings.Join(list, ", "))
		}
	}
}
Ejemplo n.º 2
0
func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) {
	tIface, _ := c.typ.Underlying().(*types.Interface)

	for ifaceObj := range delta {
		tDyn, v, indirect := a.taggedValue(ifaceObj)
		if tDyn == nil {
			panic("not a tagged value")
		}
		if indirect {
			// TODO(adonovan): we'll need to implement this
			// when we start creating indirect tagged objects.
			panic("indirect tagged object")
		}

		if tIface != nil {
			if types.IsAssignableTo(tDyn, tIface) {
				if a.addLabel(c.dst, ifaceObj) {
					a.addWork(c.dst)
				}
			}
		} else {
			if types.IsIdentical(tDyn, c.typ) {
				// Copy entire payload to dst.
				//
				// TODO(adonovan): opt: if tConc is
				// nonpointerlike we can skip this
				// entire constraint, perhaps.  We
				// only care about pointers among the
				// fields.
				a.onlineCopyN(c.dst, v, a.sizeof(tDyn))
			}
		}
	}
}
Ejemplo n.º 3
0
// Implements displays the 'implements" relation among all
// package-level named types in the package containing the query
// position.
//
// TODO(adonovan): more features:
// - should we include pairs of types belonging to
//   different packages in the 'implements' relation?
// - should we restrict the query to the type declaration identified
//   by the query position, if any, and use all types in the package
//   otherwise?
// - should we show types that are local to functions?
//   They can only have methods via promotion.
// - abbreviate the set of concrete types implementing the empty
//   interface.
// - should we scan the instruction stream for MakeInterface
//   instructions and report which concrete->interface conversions
//   actually occur, with examples?  (NB: this is not a conservative
//   answer due to ChangeInterface, i.e. subtyping among interfaces.)
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	pkg := qpos.info.Pkg

	// Compute set of named interface/concrete types at package level.
	var interfaces, concretes []*types.Named
	scope := pkg.Scope()
	for _, name := range scope.Names() {
		mem := scope.Lookup(name)
		if t, ok := mem.(*types.TypeName); ok {
			nt := t.Type().(*types.Named)
			if _, ok := nt.Underlying().(*types.Interface); ok {
				interfaces = append(interfaces, nt)
			} else {
				concretes = append(concretes, nt)
			}
		}
	}

	// For each interface, show the concrete types that implement it.
	var facts []implementsFact
	for _, iface := range interfaces {
		fact := implementsFact{iface: iface}
		for _, conc := range concretes {
			if types.IsAssignableTo(conc, iface) {
				fact.conc = conc
			} else if ptr := types.NewPointer(conc); types.IsAssignableTo(ptr, iface) {
				fact.conc = ptr
			} else {
				continue
			}
			facts = append(facts, fact)
		}
	}
	// TODO(adonovan): sort facts to ensure test nondeterminism.

	return &implementsResult{o.prog.Fset, facts}, nil
}
Ejemplo n.º 4
0
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
	for ifaceObj := range delta {
		tDyn, _, indirect := a.taggedValue(ifaceObj)
		if indirect {
			// TODO(adonovan): we'll need to implement this
			// when we start creating indirect tagged objects.
			panic("indirect tagged object")
		}

		if types.IsAssignableTo(tDyn, c.typ) {
			if a.addLabel(c.dst, ifaceObj) {
				a.addWork(c.dst)
			}
		}
	}
}
Ejemplo n.º 5
0
// Implements displays the "implements" relation as it pertains to the
// selected type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	// Find the selected type.
	// TODO(adonovan): fix: make it work on qualified Idents too.
	path, action := findInterestingNode(qpos.info, qpos.path)
	if action != actionType {
		return nil, fmt.Errorf("no type here")
	}
	T := qpos.info.TypeOf(path[0].(ast.Expr))
	if T == nil {
		return nil, fmt.Errorf("no type 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 id, obj := range info.Objects {
			if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

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

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

			// T concrete, U interface
			if types.IsAssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.IsAssignableTo(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 nondeterminism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	return &implementsResult{T, pos, to, from, fromPtr}, nil
}