+++ /dev/null
-// Copyright 2014 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 analysis
-
-// This file computes the markup for information from go/types:
-// IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and
-// the IMPLEMENTS relation.
-//
-// IMPORTS links connect import specs to the documentation for the
-// imported package.
-//
-// RESOLUTION links referring identifiers to their defining
-// identifier, and adds tooltips for kind and type.
-//
-// METHOD SETS, size/alignment, and the IMPLEMENTS relation are
-// displayed in the lower pane when a type's defining identifier is
-// clicked.
-
-import (
- "fmt"
- "go/types"
- "reflect"
- "strconv"
- "strings"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-// TODO(adonovan): audit to make sure it's safe on ill-typed packages.
-
-// TODO(adonovan): use same Sizes as loader.Config.
-var sizes = types.StdSizes{WordSize: 8, MaxAlign: 8}
-
-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 {
- if named, ok := obj.Type().(*types.Named); ok {
- a.namedType(named, implements)
- }
- }
- }
-}
-
-func (a *analysis) namedType(T *types.Named, implements map[*types.Named]implementsFacts) {
- obj := T.Obj()
- qualifier := types.RelativeTo(obj.Pkg())
- 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)
- }
-}
-
-// -- utilities --------------------------------------------------------
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if p, ok := typ.Underlying().(*types.Pointer); ok {
- return p.Elem()
- }
- return typ
-}
-
-// isPackageLevel reports whether obj is a package-level object.
-func isPackageLevel(obj types.Object) bool {
- return obj.Pkg().Scope().Lookup(obj.Name()) == obj
-}