internal/godoc: remove analysis

We have never run this code on golang.org.

Change-Id: Iba632f00ea055308c036f389e3af56ab11781832
Reviewed-on: https://go-review.googlesource.com/c/website/+/293423
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/internal/godoc/analysis/README b/internal/godoc/analysis/README
deleted file mode 100644
index d3e732e..0000000
--- a/internal/godoc/analysis/README
+++ /dev/null
@@ -1,111 +0,0 @@
-
-Type and Pointer Analysis to-do list
-====================================
-
-Alan Donovan <adonovan@google.com>
-
-
-Overall design
---------------
-
-We should re-run the type and pointer analyses periodically,
-as we do with the indexer.
-
-Version skew: how to mitigate the bad effects of stale URLs in old pages?
-We could record the file's length/CRC32/mtime in the go/loader, and
-refuse to decorate it with links unless they match at serving time.
-
-Use the VFS mechanism when (a) enumerating packages and (b) loading
-them.  (Requires planned changes to go/loader.)
-
-Future work: shard this using map/reduce for larger corpora.
-
-Testing: how does one test that a web page "looks right"?
-
-
-Bugs
-----
-
-(*ssa.Program).Create requires transitively error-free packages.  We
-can make this more robust by making the requirement transitively free
-of "hard" errors; soft errors are fine.
-
-Markup of compiler errors is slightly buggy because they overlap with
-other selections (e.g. Idents).  Fix.
-
-
-User Interface
---------------
-
-CALLGRAPH:
-- Add a search box: given a search node, expand path from each entry
-  point to it.
-- Cause hovering over a given node to highlight that node, and all
-  nodes that are logically identical to it.
-- Initially expand the callgraph trees (but not their toggle divs).
-
-CALLEES:
-- The '(' links are not very discoverable.  Highlight them?
-
-Type info:
-- In the source viewer's lower pane, use a toggle div around the
-  IMPLEMENTS and METHODSETS lists, like we do in the package view.
-  Only expand them initially if short.
-- Include IMPLEMENTS and METHOD SETS information in search index.
-- URLs in IMPLEMENTS/METHOD SETS always link to source, even from the
-  package docs view.  This makes sense for links to non-exported
-  types, but links to exported types and funcs should probably go to
-  other package docs.
-- Suppress toggle divs for empty method sets.
-
-Misc:
-- The [X] button in the lower pane is subject to scrolling.
-- Should the lower pane be floating?  An iframe?
-  When we change document.location by clicking on a link, it will go away.
-  How do we prevent that (a la Gmail's chat windows)?
-- Progress/status: for each file, display its analysis status, one of:
-   - not in analysis scope
-   - type analysis running...
-   - type analysis complete
-     (+ optionally: there were type errors in this file)
-   And if PTA requested:
-   - type analysis complete; PTA not attempted due to type errors
-   - PTA running...
-   - PTA complete
-- Scroll the selection into view, e.g. the vertical center, or better
-  still, under the pointer (assuming we have a mouse).
-
-
-More features
--------------
-
-Display the REFERRERS relation?  (Useful but potentially large.)
-
-Display the INSTANTIATIONS relation? i.e. given a type T, show the set of
-syntactic constructs that can instantiate it:
-    var x T
-    x := T{...}
-    x = new(T)
-    x = make([]T, n)
-    etc
-    + all INSTANTIATIONS of all S defined as struct{t T} or [n]T
-(Potentially a lot of information.)
-(Add this to guru too.)
-
-
-Optimisations
--------------
-
-Each call to addLink takes a (per-file) lock.  The locking is
-fine-grained so server latency isn't terrible, but overall it makes
-the link computation quite slow.  Batch update might be better.
-
-Memory usage is now about 1.5GB for GOROOT + go.tools.  It used to be 700MB.
-
-Optimize for time and space.  The main slowdown is the network I/O
-time caused by an increase in page size of about 3x: about 2x from
-HTML, and 0.7--2.1x from JSON (unindented vs indented).  The JSON
-contains a lot of filenames (e.g. 820 copies of 16 distinct
-filenames).  20% of the HTML is L%d spans (now disabled).  The HTML
-also contains lots of tooltips for long struct/interface types.
-De-dup or just abbreviate?  The actual formatting is very fast.
diff --git a/internal/godoc/analysis/analysis.go b/internal/godoc/analysis/analysis.go
deleted file mode 100644
index 94af35f..0000000
--- a/internal/godoc/analysis/analysis.go
+++ /dev/null
@@ -1,613 +0,0 @@
-// 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 performs type and pointer analysis
-// and generates mark-up for the Go source view.
-//
-// The Run method populates a Result object by running type and
-// (optionally) pointer analysis.  The Result object is thread-safe
-// and at all times may be accessed by a serving thread, even as it is
-// progressively populated as analysis facts are derived.
-//
-// The Result is a mapping from each godoc file URL
-// (e.g. /src/fmt/print.go) to information about that file.  The
-// information is a list of HTML markup links and a JSON array of
-// structured data values.  Some of the links call client-side
-// JavaScript functions that index this array.
-//
-// The analysis computes mark-up for the following relations:
-//
-// IMPORTS: for each ast.ImportSpec, the package that it denotes.
-//
-// RESOLUTION: for each ast.Ident, its kind and type, and the location
-// of its definition.
-//
-// METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
-// its method-set, the set of interfaces it implements or is
-// implemented by, and its size/align values.
-//
-// CALLERS, CALLEES: for each function declaration ('func' token), its
-// callers, and for each call-site ('(' token), its callees.
-//
-// CALLGRAPH: the package docs include an interactive viewer for the
-// intra-package call graph of "fmt".
-//
-// CHANNEL PEERS: for each channel operation make/<-/close, the set of
-// other channel ops that alias the same channel(s).
-//
-// ERRORS: for each locus of a frontend (scanner/parser/type) error, the
-// location is highlighted in red and hover text provides the compiler
-// error message.
-//
-package analysis // import "golang.org/x/website/internal/godoc/analysis"
-
-import (
-	"fmt"
-	"go/build"
-	"go/scanner"
-	"go/token"
-	"go/types"
-	"html"
-	"io"
-	"log"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-	"sync"
-
-	"golang.org/x/tools/go/loader"
-	"golang.org/x/tools/go/pointer"
-	"golang.org/x/tools/go/ssa"
-	"golang.org/x/tools/go/ssa/ssautil"
-)
-
-// -- links ------------------------------------------------------------
-
-// A Link is an HTML decoration of the bytes [Start, End) of a file.
-// Write is called before/after those bytes to emit the mark-up.
-type Link interface {
-	Start() int
-	End() int
-	Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
-}
-
-// An <a> element.
-type aLink struct {
-	start, end int    // =godoc.Segment
-	title      string // hover text
-	onclick    string // JS code (NB: trusted)
-	href       string // URL     (NB: trusted)
-}
-
-func (a aLink) Start() int { return a.start }
-func (a aLink) End() int   { return a.end }
-func (a aLink) Write(w io.Writer, _ int, start bool) {
-	if start {
-		fmt.Fprintf(w, `<a title='%s'`, html.EscapeString(a.title))
-		if a.onclick != "" {
-			fmt.Fprintf(w, ` onclick='%s'`, html.EscapeString(a.onclick))
-		}
-		if a.href != "" {
-			// TODO(adonovan): I think that in principle, a.href must first be
-			// url.QueryEscape'd, but if I do that, a leading slash becomes "%2F",
-			// which causes the browser to treat the path as relative, not absolute.
-			// WTF?
-			fmt.Fprintf(w, ` href='%s'`, html.EscapeString(a.href))
-		}
-		fmt.Fprintf(w, ">")
-	} else {
-		fmt.Fprintf(w, "</a>")
-	}
-}
-
-// An <a class='error'> element.
-type errorLink struct {
-	start int
-	msg   string
-}
-
-func (e errorLink) Start() int { return e.start }
-func (e errorLink) End() int   { return e.start + 1 }
-
-func (e errorLink) Write(w io.Writer, _ int, start bool) {
-	// <span> causes havoc, not sure why, so use <a>.
-	if start {
-		fmt.Fprintf(w, `<a class='error' title='%s'>`, html.EscapeString(e.msg))
-	} else {
-		fmt.Fprintf(w, "</a>")
-	}
-}
-
-// -- fileInfo ---------------------------------------------------------
-
-// FileInfo holds analysis information for the source file view.
-// Clients must not mutate it.
-type FileInfo struct {
-	Data  []interface{} // JSON serializable values
-	Links []Link        // HTML link markup
-}
-
-// A fileInfo is the server's store of hyperlinks and JSON data for a
-// particular file.
-type fileInfo struct {
-	mu        sync.Mutex
-	data      []interface{} // JSON objects
-	links     []Link
-	sorted    bool
-	hasErrors bool // TODO(adonovan): surface this in the UI
-}
-
-// addLink adds a link to the Go source file fi.
-func (fi *fileInfo) addLink(link Link) {
-	fi.mu.Lock()
-	fi.links = append(fi.links, link)
-	fi.sorted = false
-	if _, ok := link.(errorLink); ok {
-		fi.hasErrors = true
-	}
-	fi.mu.Unlock()
-}
-
-// addData adds the structured value x to the JSON data for the Go
-// source file fi.  Its index is returned.
-func (fi *fileInfo) addData(x interface{}) int {
-	fi.mu.Lock()
-	index := len(fi.data)
-	fi.data = append(fi.data, x)
-	fi.mu.Unlock()
-	return index
-}
-
-// get returns the file info in external form.
-// Callers must not mutate its fields.
-func (fi *fileInfo) get() FileInfo {
-	var r FileInfo
-	// Copy slices, to avoid races.
-	fi.mu.Lock()
-	r.Data = append(r.Data, fi.data...)
-	if !fi.sorted {
-		sort.Sort(linksByStart(fi.links))
-		fi.sorted = true
-	}
-	r.Links = append(r.Links, fi.links...)
-	fi.mu.Unlock()
-	return r
-}
-
-// PackageInfo holds analysis information for the package view.
-// Clients must not mutate it.
-type PackageInfo struct {
-	CallGraph      []*PCGNodeJSON
-	CallGraphIndex map[string]int
-	Types          []*TypeInfoJSON
-}
-
-type pkgInfo struct {
-	mu             sync.Mutex
-	callGraph      []*PCGNodeJSON
-	callGraphIndex map[string]int  // keys are (*ssa.Function).RelString()
-	types          []*TypeInfoJSON // type info for exported types
-}
-
-func (pi *pkgInfo) setCallGraph(callGraph []*PCGNodeJSON, callGraphIndex map[string]int) {
-	pi.mu.Lock()
-	pi.callGraph = callGraph
-	pi.callGraphIndex = callGraphIndex
-	pi.mu.Unlock()
-}
-
-func (pi *pkgInfo) addType(t *TypeInfoJSON) {
-	pi.mu.Lock()
-	pi.types = append(pi.types, t)
-	pi.mu.Unlock()
-}
-
-// get returns the package info in external form.
-// Callers must not mutate its fields.
-func (pi *pkgInfo) get() PackageInfo {
-	var r PackageInfo
-	// Copy slices, to avoid races.
-	pi.mu.Lock()
-	r.CallGraph = append(r.CallGraph, pi.callGraph...)
-	r.CallGraphIndex = pi.callGraphIndex
-	r.Types = append(r.Types, pi.types...)
-	pi.mu.Unlock()
-	return r
-}
-
-// -- Result -----------------------------------------------------------
-
-// Result contains the results of analysis.
-// The result contains a mapping from filenames to a set of HTML links
-// and JavaScript data referenced by the links.
-type Result struct {
-	mu        sync.Mutex           // guards maps (but not their contents)
-	status    string               // global analysis status
-	fileInfos map[string]*fileInfo // keys are godoc file URLs
-	pkgInfos  map[string]*pkgInfo  // keys are import paths
-}
-
-// fileInfo returns the fileInfo for the specified godoc file URL,
-// constructing it as needed.  Thread-safe.
-func (res *Result) fileInfo(url string) *fileInfo {
-	res.mu.Lock()
-	fi, ok := res.fileInfos[url]
-	if !ok {
-		if res.fileInfos == nil {
-			res.fileInfos = make(map[string]*fileInfo)
-		}
-		fi = new(fileInfo)
-		res.fileInfos[url] = fi
-	}
-	res.mu.Unlock()
-	return fi
-}
-
-// Status returns a human-readable description of the current analysis status.
-func (res *Result) Status() string {
-	res.mu.Lock()
-	defer res.mu.Unlock()
-	return res.status
-}
-
-func (res *Result) setStatusf(format string, args ...interface{}) {
-	res.mu.Lock()
-	res.status = fmt.Sprintf(format, args...)
-	log.Printf(format, args...)
-	res.mu.Unlock()
-}
-
-// FileInfo returns new slices containing opaque JSON values and the
-// HTML link markup for the specified godoc file URL.  Thread-safe.
-// Callers must not mutate the elements.
-// It returns "zero" if no data is available.
-//
-func (res *Result) FileInfo(url string) (fi FileInfo) {
-	return res.fileInfo(url).get()
-}
-
-// pkgInfo returns the pkgInfo for the specified import path,
-// constructing it as needed.  Thread-safe.
-func (res *Result) pkgInfo(importPath string) *pkgInfo {
-	res.mu.Lock()
-	pi, ok := res.pkgInfos[importPath]
-	if !ok {
-		if res.pkgInfos == nil {
-			res.pkgInfos = make(map[string]*pkgInfo)
-		}
-		pi = new(pkgInfo)
-		res.pkgInfos[importPath] = pi
-	}
-	res.mu.Unlock()
-	return pi
-}
-
-// PackageInfo returns new slices of JSON values for the callgraph and
-// type info for the specified package.  Thread-safe.
-// Callers must not mutate its fields.
-// PackageInfo returns "zero" if no data is available.
-//
-func (res *Result) PackageInfo(importPath string) PackageInfo {
-	return res.pkgInfo(importPath).get()
-}
-
-// -- analysis ---------------------------------------------------------
-
-type analysis struct {
-	result    *Result
-	prog      *ssa.Program
-	ops       []chanOp       // all channel ops in program
-	allNamed  []*types.Named // all "defined" (formerly "named") types in the program
-	ptaConfig pointer.Config
-	path2url  map[string]string // maps openable path to godoc file URL (/src/fmt/print.go)
-	pcgs      map[*ssa.Package]*packageCallGraph
-}
-
-// fileAndOffset returns the file and offset for a given pos.
-func (a *analysis) fileAndOffset(pos token.Pos) (fi *fileInfo, offset int) {
-	return a.fileAndOffsetPosn(a.prog.Fset.Position(pos))
-}
-
-// fileAndOffsetPosn returns the file and offset for a given position.
-func (a *analysis) fileAndOffsetPosn(posn token.Position) (fi *fileInfo, offset int) {
-	url := a.path2url[posn.Filename]
-	return a.result.fileInfo(url), posn.Offset
-}
-
-// posURL returns the URL of the source extent [pos, pos+len).
-func (a *analysis) posURL(pos token.Pos, len int) string {
-	if pos == token.NoPos {
-		return ""
-	}
-	posn := a.prog.Fset.Position(pos)
-	url := a.path2url[posn.Filename]
-	return fmt.Sprintf("%s?s=%d:%d#L%d",
-		url, posn.Offset, posn.Offset+len, posn.Line)
-}
-
-// ----------------------------------------------------------------------
-
-// Run runs program analysis and computes the resulting markup,
-// populating *result in a thread-safe manner, first with type
-// information then later with pointer analysis information if
-// enabled by the pta flag.
-//
-func Run(pta bool, result *Result) {
-	conf := loader.Config{
-		AllowErrors: true,
-	}
-
-	// Silence the default error handler.
-	// Don't print all errors; we'll report just
-	// one per errant package later.
-	conf.TypeChecker.Error = func(e error) {}
-
-	var roots, args []string // roots[i] ends with os.PathSeparator
-
-	// Enumerate packages in $GOROOT.
-	root := filepath.Join(build.Default.GOROOT, "src") + string(os.PathSeparator)
-	roots = append(roots, root)
-	args = allPackages(root)
-	log.Printf("GOROOT=%s: %s\n", root, args)
-
-	// Enumerate packages in $GOPATH.
-	for i, dir := range filepath.SplitList(build.Default.GOPATH) {
-		root := filepath.Join(dir, "src") + string(os.PathSeparator)
-		roots = append(roots, root)
-		pkgs := allPackages(root)
-		log.Printf("GOPATH[%d]=%s: %s\n", i, root, pkgs)
-		args = append(args, pkgs...)
-	}
-
-	// Uncomment to make startup quicker during debugging.
-	//args = []string{"golang.org/x/tools/cmd/godoc"}
-	//args = []string{"fmt"}
-
-	if _, err := conf.FromArgs(args, true); err != nil {
-		// TODO(adonovan): degrade gracefully, not fail totally.
-		// (The crippling case is a parse error in an external test file.)
-		result.setStatusf("Analysis failed: %s.", err) // import error
-		return
-	}
-
-	result.setStatusf("Loading and type-checking packages...")
-	iprog, err := conf.Load()
-	if iprog != nil {
-		// Report only the first error of each package.
-		for _, info := range iprog.AllPackages {
-			for _, err := range info.Errors {
-				fmt.Fprintln(os.Stderr, err)
-				break
-			}
-		}
-		log.Printf("Loaded %d packages.", len(iprog.AllPackages))
-	}
-	if err != nil {
-		result.setStatusf("Loading failed: %s.\n", err)
-		return
-	}
-
-	// Create SSA-form program representation.
-	// Only the transitively error-free packages are used.
-	prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
-
-	// Create a "testmain" package for each package with tests.
-	for _, pkg := range prog.AllPackages() {
-		if testmain := prog.CreateTestMainPackage(pkg); testmain != nil {
-			log.Printf("Adding tests for %s", pkg.Pkg.Path())
-		}
-	}
-
-	// Build SSA code for bodies of all functions in the whole program.
-	result.setStatusf("Constructing SSA form...")
-	prog.Build()
-	log.Print("SSA construction complete")
-
-	a := analysis{
-		result: result,
-		prog:   prog,
-		pcgs:   make(map[*ssa.Package]*packageCallGraph),
-	}
-
-	// Build a mapping from openable filenames to godoc file URLs,
-	// i.e. "/src/" plus path relative to GOROOT/src or GOPATH[i]/src.
-	a.path2url = make(map[string]string)
-	for _, info := range iprog.AllPackages {
-	nextfile:
-		for _, f := range info.Files {
-			if f.Pos() == 0 {
-				continue // e.g. files generated by cgo
-			}
-			abs := iprog.Fset.File(f.Pos()).Name()
-			// Find the root to which this file belongs.
-			for _, root := range roots {
-				rel := strings.TrimPrefix(abs, root)
-				if len(rel) < len(abs) {
-					a.path2url[abs] = "/src/" + filepath.ToSlash(rel)
-					continue nextfile
-				}
-			}
-
-			log.Printf("Can't locate file %s (package %q) beneath any root",
-				abs, info.Pkg.Path())
-		}
-	}
-
-	// Add links for scanner, parser, type-checker errors.
-	// TODO(adonovan): fix: these links can overlap with
-	// identifier markup, causing the renderer to emit some
-	// characters twice.
-	errors := make(map[token.Position][]string)
-	for _, info := range iprog.AllPackages {
-		for _, err := range info.Errors {
-			switch err := err.(type) {
-			case types.Error:
-				posn := a.prog.Fset.Position(err.Pos)
-				errors[posn] = append(errors[posn], err.Msg)
-			case scanner.ErrorList:
-				for _, e := range err {
-					errors[e.Pos] = append(errors[e.Pos], e.Msg)
-				}
-			default:
-				log.Printf("Package %q has error (%T) without position: %v\n",
-					info.Pkg.Path(), err, err)
-			}
-		}
-	}
-	for posn, errs := range errors {
-		fi, offset := a.fileAndOffsetPosn(posn)
-		fi.addLink(errorLink{
-			start: offset,
-			msg:   strings.Join(errs, "\n"),
-		})
-	}
-
-	// ---------- type-based analyses ----------
-
-	// Compute the all-pairs IMPLEMENTS relation.
-	// Collect all named types, even local types
-	// (which can have methods via promotion)
-	// and the built-in "error".
-	errorType := types.Universe.Lookup("error").Type().(*types.Named)
-	a.allNamed = append(a.allNamed, errorType)
-	for _, info := range iprog.AllPackages {
-		for _, obj := range info.Defs {
-			if obj, ok := obj.(*types.TypeName); ok {
-				if named, ok := obj.Type().(*types.Named); ok {
-					a.allNamed = append(a.allNamed, named)
-				}
-			}
-		}
-	}
-	log.Print("Computing implements relation...")
-	facts := computeImplements(&a.prog.MethodSets, a.allNamed)
-
-	// Add the type-based analysis results.
-	log.Print("Extracting type info...")
-	for _, info := range iprog.AllPackages {
-		a.doTypeInfo(info, facts)
-	}
-
-	a.visitInstrs(pta)
-
-	result.setStatusf("Type analysis complete.")
-
-	if pta {
-		mainPkgs := ssautil.MainPackages(prog.AllPackages())
-		log.Print("Transitively error-free main packages: ", mainPkgs)
-		a.pointer(mainPkgs)
-	}
-}
-
-// visitInstrs visits all SSA instructions in the program.
-func (a *analysis) visitInstrs(pta bool) {
-	log.Print("Visit instructions...")
-	for fn := range ssautil.AllFunctions(a.prog) {
-		for _, b := range fn.Blocks {
-			for _, instr := range b.Instrs {
-				// CALLEES (static)
-				// (Dynamic calls require pointer analysis.)
-				//
-				// We use the SSA representation to find the static callee,
-				// since in many cases it does better than the
-				// types.Info.{Refs,Selection} information.  For example:
-				//
-				//   defer func(){}()      // static call to anon function
-				//   f := func(){}; f()    // static call to anon function
-				//   f := fmt.Println; f() // static call to named function
-				//
-				// The downside is that we get no static callee information
-				// for packages that (transitively) contain errors.
-				if site, ok := instr.(ssa.CallInstruction); ok {
-					if callee := site.Common().StaticCallee(); callee != nil {
-						// TODO(adonovan): callgraph: elide wrappers.
-						// (Do static calls ever go to wrappers?)
-						if site.Common().Pos() != token.NoPos {
-							a.addCallees(site, []*ssa.Function{callee})
-						}
-					}
-				}
-
-				if !pta {
-					continue
-				}
-
-				// CHANNEL PEERS
-				// Collect send/receive/close instructions in the whole ssa.Program.
-				for _, op := range chanOps(instr) {
-					a.ops = append(a.ops, op)
-					a.ptaConfig.AddQuery(op.ch) // add channel ssa.Value to PTA query
-				}
-			}
-		}
-	}
-	log.Print("Visit instructions complete")
-}
-
-// pointer runs the pointer analysis.
-func (a *analysis) pointer(mainPkgs []*ssa.Package) {
-	// Run the pointer analysis and build the complete callgraph.
-	a.ptaConfig.Mains = mainPkgs
-	a.ptaConfig.BuildCallGraph = true
-	a.ptaConfig.Reflection = false // (for now)
-
-	a.result.setStatusf("Pointer analysis running...")
-
-	ptares, err := pointer.Analyze(&a.ptaConfig)
-	if err != nil {
-		// If this happens, it indicates a bug.
-		a.result.setStatusf("Pointer analysis failed: %s.", err)
-		return
-	}
-	log.Print("Pointer analysis complete.")
-
-	// Add the results of pointer analysis.
-
-	a.result.setStatusf("Computing channel peers...")
-	a.doChannelPeers(ptares.Queries)
-	a.result.setStatusf("Computing dynamic call graph edges...")
-	a.doCallgraph(ptares.CallGraph)
-
-	a.result.setStatusf("Analysis complete.")
-}
-
-type linksByStart []Link
-
-func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
-func (a linksByStart) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a linksByStart) Len() int           { return len(a) }
-
-// allPackages returns a new sorted slice of all packages beneath the
-// specified package root directory, e.g. $GOROOT/src or $GOPATH/src.
-// Derived from from go/ssa/stdlib_test.go
-// root must end with os.PathSeparator.
-//
-// TODO(adonovan): use buildutil.AllPackages when the tree thaws.
-func allPackages(root string) []string {
-	var pkgs []string
-	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
-		if info == nil {
-			return nil // non-existent root directory?
-		}
-		if !info.IsDir() {
-			return nil // not a directory
-		}
-		// Prune the search if we encounter any of these names:
-		base := filepath.Base(path)
-		if base == "testdata" || strings.HasPrefix(base, ".") {
-			return filepath.SkipDir
-		}
-		pkg := filepath.ToSlash(strings.TrimPrefix(path, root))
-		switch pkg {
-		case "builtin":
-			return filepath.SkipDir
-		case "":
-			return nil // ignore root of tree
-		}
-		pkgs = append(pkgs, pkg)
-		return nil
-	})
-	return pkgs
-}
diff --git a/internal/godoc/analysis/callgraph.go b/internal/godoc/analysis/callgraph.go
deleted file mode 100644
index 492022d..0000000
--- a/internal/godoc/analysis/callgraph.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// 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 CALLERS and CALLEES relations from the call
-// graph.  CALLERS/CALLEES information is displayed in the lower pane
-// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
-
-import (
-	"fmt"
-	"go/ast"
-	"go/token"
-	"go/types"
-	"log"
-	"math/big"
-	"sort"
-
-	"golang.org/x/tools/go/callgraph"
-	"golang.org/x/tools/go/ssa"
-)
-
-// doCallgraph computes the CALLEES and CALLERS relations.
-func (a *analysis) doCallgraph(cg *callgraph.Graph) {
-	log.Print("Deleting synthetic nodes...")
-	// TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
-	// inefficient and can be (unpredictably) slow.
-	cg.DeleteSyntheticNodes()
-	log.Print("Synthetic nodes deleted")
-
-	// Populate nodes of package call graphs (PCGs).
-	for _, n := range cg.Nodes {
-		a.pcgAddNode(n.Func)
-	}
-	// Within each PCG, sort funcs by name.
-	for _, pcg := range a.pcgs {
-		pcg.sortNodes()
-	}
-
-	calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
-	callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
-	for _, n := range cg.Nodes {
-		for _, e := range n.Out {
-			if e.Site == nil {
-				continue // a call from a synthetic node such as <root>
-			}
-
-			// Add (site pos, callee) to calledFuncs.
-			// (Dynamic calls only.)
-			callee := e.Callee.Func
-
-			a.pcgAddEdge(n.Func, callee)
-
-			if callee.Synthetic != "" {
-				continue // call of a package initializer
-			}
-
-			if e.Site.Common().StaticCallee() == nil {
-				// dynamic call
-				// (CALLEES information for static calls
-				// is computed using SSA information.)
-				lparen := e.Site.Common().Pos()
-				if lparen != token.NoPos {
-					fns := calledFuncs[e.Site]
-					if fns == nil {
-						fns = make(map[*ssa.Function]bool)
-						calledFuncs[e.Site] = fns
-					}
-					fns[callee] = true
-				}
-			}
-
-			// Add (callee, site) to callingSites.
-			fns := callingSites[callee]
-			if fns == nil {
-				fns = make(map[ssa.CallInstruction]bool)
-				callingSites[callee] = fns
-			}
-			fns[e.Site] = true
-		}
-	}
-
-	// CALLEES.
-	log.Print("Callees...")
-	for site, fns := range calledFuncs {
-		var funcs funcsByPos
-		for fn := range fns {
-			funcs = append(funcs, fn)
-		}
-		sort.Sort(funcs)
-
-		a.addCallees(site, funcs)
-	}
-
-	// CALLERS
-	log.Print("Callers...")
-	for callee, sites := range callingSites {
-		pos := funcToken(callee)
-		if pos == token.NoPos {
-			log.Printf("CALLERS: skipping %s: no pos", callee)
-			continue
-		}
-
-		var this *types.Package // for relativizing names
-		if callee.Pkg != nil {
-			this = callee.Pkg.Pkg
-		}
-
-		// Compute sites grouped by parent, with text and URLs.
-		sitesByParent := make(map[*ssa.Function]sitesByPos)
-		for site := range sites {
-			fn := site.Parent()
-			sitesByParent[fn] = append(sitesByParent[fn], site)
-		}
-		var funcs funcsByPos
-		for fn := range sitesByParent {
-			funcs = append(funcs, fn)
-		}
-		sort.Sort(funcs)
-
-		v := callersJSON{
-			Callee:  callee.String(),
-			Callers: []callerJSON{}, // (JS wants non-nil)
-		}
-		for _, fn := range funcs {
-			caller := callerJSON{
-				Func:  prettyFunc(this, fn),
-				Sites: []anchorJSON{}, // (JS wants non-nil)
-			}
-			sites := sitesByParent[fn]
-			sort.Sort(sites)
-			for _, site := range sites {
-				pos := site.Common().Pos()
-				if pos != token.NoPos {
-					caller.Sites = append(caller.Sites, anchorJSON{
-						Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
-						Href: a.posURL(pos, len("(")),
-					})
-				}
-			}
-			v.Callers = append(v.Callers, caller)
-		}
-
-		fi, offset := a.fileAndOffset(pos)
-		fi.addLink(aLink{
-			start:   offset,
-			end:     offset + len("func"),
-			title:   fmt.Sprintf("%d callers", len(sites)),
-			onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
-		})
-	}
-
-	// PACKAGE CALLGRAPH
-	log.Print("Package call graph...")
-	for pkg, pcg := range a.pcgs {
-		// Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
-		index := make(map[string]int)
-
-		// Treat exported functions (and exported methods of
-		// exported named types) as roots even if they aren't
-		// actually called from outside the package.
-		for i, n := range pcg.nodes {
-			if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
-				continue
-			}
-			recv := n.fn.Signature.Recv()
-			if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
-				roots := &pcg.nodes[0].edges
-				roots.SetBit(roots, i, 1)
-			}
-			index[n.fn.RelString(pkg.Pkg)] = i
-		}
-
-		json := a.pcgJSON(pcg)
-
-		// TODO(adonovan): pkg.Path() is not unique!
-		// It is possible to declare a non-test package called x_test.
-		a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
-	}
-}
-
-// addCallees adds client data and links for the facts that site calls fns.
-func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
-	v := calleesJSON{
-		Descr:   site.Common().Description(),
-		Callees: []anchorJSON{}, // (JS wants non-nil)
-	}
-	var this *types.Package // for relativizing names
-	if p := site.Parent().Package(); p != nil {
-		this = p.Pkg
-	}
-
-	for _, fn := range fns {
-		v.Callees = append(v.Callees, anchorJSON{
-			Text: prettyFunc(this, fn),
-			Href: a.posURL(funcToken(fn), len("func")),
-		})
-	}
-
-	fi, offset := a.fileAndOffset(site.Common().Pos())
-	fi.addLink(aLink{
-		start:   offset,
-		end:     offset + len("("),
-		title:   fmt.Sprintf("%d callees", len(v.Callees)),
-		onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
-	})
-}
-
-// -- utilities --------------------------------------------------------
-
-// stable order within packages but undefined across packages.
-type funcsByPos []*ssa.Function
-
-func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
-func (a funcsByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a funcsByPos) Len() int           { return len(a) }
-
-type sitesByPos []ssa.CallInstruction
-
-func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
-func (a sitesByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a sitesByPos) Len() int           { return len(a) }
-
-func funcToken(fn *ssa.Function) token.Pos {
-	switch syntax := fn.Syntax().(type) {
-	case *ast.FuncLit:
-		return syntax.Type.Func
-	case *ast.FuncDecl:
-		return syntax.Type.Func
-	}
-	return token.NoPos
-}
-
-// 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)
-}
-
-// -- intra-package callgraph ------------------------------------------
-
-// pcgNode represents a node in the package call graph (PCG).
-type pcgNode struct {
-	fn     *ssa.Function
-	pretty string  // cache of prettyFunc(fn)
-	edges  big.Int // set of callee func indices
-}
-
-// A packageCallGraph represents the intra-package edges of the global call graph.
-// The zeroth node indicates "all external functions".
-type packageCallGraph struct {
-	nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
-	nodes     []*pcgNode            // maps node index to node
-}
-
-// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
-func (pcg *packageCallGraph) sortNodes() {
-	nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
-	nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
-	for fn := range pcg.nodeIndex {
-		nodes = append(nodes, &pcgNode{
-			fn:     fn,
-			pretty: prettyFunc(fn.Pkg.Pkg, fn),
-		})
-	}
-	sort.Sort(pcgNodesByPretty(nodes[1:]))
-	for i, n := range nodes {
-		pcg.nodeIndex[n.fn] = i
-	}
-	pcg.nodes = nodes
-}
-
-func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
-	var callerIndex int
-	if caller.Pkg == callee.Pkg {
-		// intra-package edge
-		callerIndex = pcg.nodeIndex[caller]
-		if callerIndex < 1 {
-			panic(caller)
-		}
-	}
-	edges := &pcg.nodes[callerIndex].edges
-	edges.SetBit(edges, pcg.nodeIndex[callee], 1)
-}
-
-func (a *analysis) pcgAddNode(fn *ssa.Function) {
-	if fn.Pkg == nil {
-		return
-	}
-	pcg, ok := a.pcgs[fn.Pkg]
-	if !ok {
-		pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
-		a.pcgs[fn.Pkg] = pcg
-	}
-	pcg.nodeIndex[fn] = -1
-}
-
-func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
-	if callee.Pkg != nil {
-		a.pcgs[callee.Pkg].addEdge(caller, callee)
-	}
-}
-
-// pcgJSON returns a new slice of callgraph JSON values.
-func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
-	var nodes []*PCGNodeJSON
-	for _, n := range pcg.nodes {
-
-		// TODO(adonovan): why is there no good way to iterate
-		// over the set bits of a big.Int?
-		var callees []int
-		nbits := n.edges.BitLen()
-		for j := 0; j < nbits; j++ {
-			if n.edges.Bit(j) == 1 {
-				callees = append(callees, j)
-			}
-		}
-
-		var pos token.Pos
-		if n.fn != nil {
-			pos = funcToken(n.fn)
-		}
-		nodes = append(nodes, &PCGNodeJSON{
-			Func: anchorJSON{
-				Text: n.pretty,
-				Href: a.posURL(pos, len("func")),
-			},
-			Callees: callees,
-		})
-	}
-	return nodes
-}
-
-type pcgNodesByPretty []*pcgNode
-
-func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
-func (a pcgNodesByPretty) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a pcgNodesByPretty) Len() int           { return len(a) }
diff --git a/internal/godoc/analysis/implements.go b/internal/godoc/analysis/implements.go
deleted file mode 100644
index 5a29579..0000000
--- a/internal/godoc/analysis/implements.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// 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 "implements" relation over all pairs of
-// named types in the program.  (The mark-up is done by typeinfo.go.)
-
-// TODO(adonovan): do we want to report implements(C, I) where C and I
-// belong to different packages and at least one is not exported?
-
-import (
-	"go/types"
-	"sort"
-
-	"golang.org/x/tools/go/types/typeutil"
-)
-
-// computeImplements computes the "implements" relation over all pairs
-// of named types in allNamed.
-func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
-	// Information about a single type's method set.
-	type msetInfo struct {
-		typ          types.Type
-		mset         *types.MethodSet
-		mask1, mask2 uint64
-	}
-
-	initMsetInfo := func(info *msetInfo, typ types.Type) {
-		info.typ = typ
-		info.mset = cache.MethodSet(typ)
-		for i := 0; i < info.mset.Len(); i++ {
-			name := info.mset.At(i).Obj().Name()
-			info.mask1 |= 1 << methodBit(name[0])
-			info.mask2 |= 1 << methodBit(name[len(name)-1])
-		}
-	}
-
-	// satisfies(T, U) reports whether type T satisfies type U.
-	// U must be an interface.
-	//
-	// Since there are thousands of types (and thus millions of
-	// pairs of types) and types.Assignable(T, U) is relatively
-	// expensive, we compute assignability directly from the
-	// method sets.  (At least one of T and U must be an
-	// interface.)
-	//
-	// We use a trick (thanks gri!) related to a Bloom filter to
-	// quickly reject most tests, which are false.  For each
-	// method set, we precompute a mask, a set of bits, one per
-	// distinct initial byte of each method name.  Thus the mask
-	// for io.ReadWriter would be {'R','W'}.  AssignableTo(T, U)
-	// cannot be true unless mask(T)&mask(U)==mask(U).
-	//
-	// As with a Bloom filter, we can improve precision by testing
-	// additional hashes, e.g. using the last letter of each
-	// method name, so long as the subset mask property holds.
-	//
-	// When analyzing the standard library, there are about 1e6
-	// calls to satisfies(), of which 0.6% return true.  With a
-	// 1-hash filter, 95% of calls avoid the expensive check; with
-	// a 2-hash filter, this grows to 98.2%.
-	satisfies := func(T, U *msetInfo) bool {
-		return T.mask1&U.mask1 == U.mask1 &&
-			T.mask2&U.mask2 == U.mask2 &&
-			containsAllIdsOf(T.mset, U.mset)
-	}
-
-	// Information about a named type N, and perhaps also *N.
-	type namedInfo struct {
-		isInterface bool
-		base        msetInfo // N
-		ptr         msetInfo // *N, iff N !isInterface
-	}
-
-	var infos []namedInfo
-
-	// Precompute the method sets and their masks.
-	for _, N := range allNamed {
-		var info namedInfo
-		initMsetInfo(&info.base, N)
-		_, info.isInterface = N.Underlying().(*types.Interface)
-		if !info.isInterface {
-			initMsetInfo(&info.ptr, types.NewPointer(N))
-		}
-
-		if info.base.mask1|info.ptr.mask1 == 0 {
-			continue // neither N nor *N has methods
-		}
-
-		infos = append(infos, info)
-	}
-
-	facts := make(map[*types.Named]implementsFacts)
-
-	// Test all pairs of distinct named types (T, U).
-	// TODO(adonovan): opt: compute (U, T) at the same time.
-	for t := range infos {
-		T := &infos[t]
-		var to, from, fromPtr []types.Type
-		for u := range infos {
-			if t == u {
-				continue
-			}
-			U := &infos[u]
-			switch {
-			case T.isInterface && U.isInterface:
-				if satisfies(&U.base, &T.base) {
-					to = append(to, U.base.typ)
-				}
-				if satisfies(&T.base, &U.base) {
-					from = append(from, U.base.typ)
-				}
-			case T.isInterface: // U concrete
-				if satisfies(&U.base, &T.base) {
-					to = append(to, U.base.typ)
-				} else if satisfies(&U.ptr, &T.base) {
-					to = append(to, U.ptr.typ)
-				}
-			case U.isInterface: // T concrete
-				if satisfies(&T.base, &U.base) {
-					from = append(from, U.base.typ)
-				} else if satisfies(&T.ptr, &U.base) {
-					fromPtr = append(fromPtr, U.base.typ)
-				}
-			}
-		}
-
-		// Sort types (arbitrarily) to avoid nondeterminism.
-		sort.Sort(typesByString(to))
-		sort.Sort(typesByString(from))
-		sort.Sort(typesByString(fromPtr))
-
-		facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
-	}
-
-	return facts
-}
-
-type implementsFacts struct {
-	to      []types.Type // named or ptr-to-named types assignable to interface T
-	from    []types.Type // named interfaces assignable from T
-	fromPtr []types.Type // named interfaces assignable only from *T
-}
-
-type typesByString []types.Type
-
-func (p typesByString) Len() int           { return len(p) }
-func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
-func (p typesByString) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-
-// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
-func methodBit(x byte) uint64 {
-	switch {
-	case 'a' <= x && x <= 'z':
-		return uint64(x - 'a')
-	case 'A' <= x && x <= 'Z':
-		return uint64(26 + x - 'A')
-	}
-	return 52 // all other bytes
-}
-
-// containsAllIdsOf reports whether the method identifiers of T are a
-// superset of those in U.  If U belongs to an interface type, the
-// result is equal to types.Assignable(T, U), but is cheaper to compute.
-//
-// TODO(gri): make this a method of *types.MethodSet.
-//
-func containsAllIdsOf(T, U *types.MethodSet) bool {
-	t, tlen := 0, T.Len()
-	u, ulen := 0, U.Len()
-	for t < tlen && u < ulen {
-		tMeth := T.At(t).Obj()
-		uMeth := U.At(u).Obj()
-		tId := tMeth.Id()
-		uId := uMeth.Id()
-		if tId > uId {
-			// U has a method T lacks: fail.
-			return false
-		}
-		if tId < uId {
-			// T has a method U lacks: ignore it.
-			t++
-			continue
-		}
-		// U and T both have a method of this Id.  Check types.
-		if !types.Identical(tMeth.Type(), uMeth.Type()) {
-			return false // type mismatch
-		}
-		u++
-		t++
-	}
-	return u == ulen
-}
diff --git a/internal/godoc/analysis/json.go b/internal/godoc/analysis/json.go
deleted file mode 100644
index f897618..0000000
--- a/internal/godoc/analysis/json.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 defines types used by client-side JavaScript.
-
-type anchorJSON struct {
-	Text string // HTML
-	Href string // URL
-}
-
-type commOpJSON struct {
-	Op anchorJSON
-	Fn string
-}
-
-// JavaScript's onClickComm() expects a commJSON.
-type commJSON struct {
-	Ops []commOpJSON
-}
-
-// Indicates one of these forms of fact about a type T:
-// T "is implemented by <ByKind> type <Other>"  (ByKind != "", e.g. "array")
-// T "implements <Other>"                       (ByKind == "")
-type implFactJSON struct {
-	ByKind string `json:",omitempty"`
-	Other  anchorJSON
-}
-
-// Implements facts are grouped by form, for ease of reading.
-type implGroupJSON struct {
-	Descr string
-	Facts []implFactJSON
-}
-
-// JavaScript's onClickIdent() expects a TypeInfoJSON.
-type TypeInfoJSON struct {
-	Name        string // type name
-	Size, Align int64
-	Methods     []anchorJSON
-	ImplGroups  []implGroupJSON
-}
-
-// JavaScript's onClickCallees() expects a calleesJSON.
-type calleesJSON struct {
-	Descr   string
-	Callees []anchorJSON // markup for called function
-}
-
-type callerJSON struct {
-	Func  string
-	Sites []anchorJSON
-}
-
-// JavaScript's onClickCallers() expects a callersJSON.
-type callersJSON struct {
-	Callee  string
-	Callers []callerJSON
-}
-
-// JavaScript's cgAddChild requires a global array of PCGNodeJSON
-// called CALLGRAPH, representing the intra-package call graph.
-// The first element is special and represents "all external callers".
-type PCGNodeJSON struct {
-	Func    anchorJSON
-	Callees []int // indices within CALLGRAPH of nodes called by this one
-}
diff --git a/internal/godoc/analysis/peers.go b/internal/godoc/analysis/peers.go
deleted file mode 100644
index a742f06..0000000
--- a/internal/godoc/analysis/peers.go
+++ /dev/null
@@ -1,154 +0,0 @@
-// 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 channel "peers" relation over all pairs of
-// channel operations in the program.  The peers are displayed in the
-// lower pane when a channel operation (make, <-, close) is clicked.
-
-// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
-// then enable reflection in PTA.
-
-import (
-	"fmt"
-	"go/token"
-	"go/types"
-
-	"golang.org/x/tools/go/pointer"
-	"golang.org/x/tools/go/ssa"
-)
-
-func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
-	addSendRecv := func(j *commJSON, op chanOp) {
-		j.Ops = append(j.Ops, commOpJSON{
-			Op: anchorJSON{
-				Text: op.mode,
-				Href: a.posURL(op.pos, op.len),
-			},
-			Fn: prettyFunc(nil, op.fn),
-		})
-	}
-
-	// Build an undirected bipartite multigraph (binary relation)
-	// of MakeChan ops and send/recv/close ops.
-	//
-	// TODO(adonovan): opt: use channel element types to partition
-	// the O(n^2) problem into subproblems.
-	aliasedOps := make(map[*ssa.MakeChan][]chanOp)
-	opToMakes := make(map[chanOp][]*ssa.MakeChan)
-	for _, op := range a.ops {
-		// Combine the PT sets from all contexts.
-		var makes []*ssa.MakeChan // aliased ops
-		ptr, ok := ptsets[op.ch]
-		if !ok {
-			continue // e.g. channel op in dead code
-		}
-		for _, label := range ptr.PointsTo().Labels() {
-			makechan, ok := label.Value().(*ssa.MakeChan)
-			if !ok {
-				continue // skip intrinsically-created channels for now
-			}
-			if makechan.Pos() == token.NoPos {
-				continue // not possible?
-			}
-			makes = append(makes, makechan)
-			aliasedOps[makechan] = append(aliasedOps[makechan], op)
-		}
-		opToMakes[op] = makes
-	}
-
-	// Now that complete relation is built, build links for ops.
-	for _, op := range a.ops {
-		v := commJSON{
-			Ops: []commOpJSON{}, // (JS wants non-nil)
-		}
-		ops := make(map[chanOp]bool)
-		for _, makechan := range opToMakes[op] {
-			v.Ops = append(v.Ops, commOpJSON{
-				Op: anchorJSON{
-					Text: "made",
-					Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
-						len("make")),
-				},
-				Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
-			})
-			for _, op := range aliasedOps[makechan] {
-				ops[op] = true
-			}
-		}
-		for op := range ops {
-			addSendRecv(&v, op)
-		}
-
-		// Add links for each aliased op.
-		fi, offset := a.fileAndOffset(op.pos)
-		fi.addLink(aLink{
-			start:   offset,
-			end:     offset + op.len,
-			title:   "show channel ops",
-			onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
-		})
-	}
-	// Add links for makechan ops themselves.
-	for makechan, ops := range aliasedOps {
-		v := commJSON{
-			Ops: []commOpJSON{}, // (JS wants non-nil)
-		}
-		for _, op := range ops {
-			addSendRecv(&v, op)
-		}
-
-		fi, offset := a.fileAndOffset(makechan.Pos())
-		fi.addLink(aLink{
-			start:   offset - len("make"),
-			end:     offset,
-			title:   "show channel ops",
-			onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
-		})
-	}
-}
-
-// -- utilities --------------------------------------------------------
-
-// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
-// Derived from cmd/guru/peers.go.
-type chanOp struct {
-	ch   ssa.Value
-	mode string // sent|received|closed
-	pos  token.Pos
-	len  int
-	fn   *ssa.Function
-}
-
-// chanOps returns a slice of all the channel operations in the instruction.
-// Derived from cmd/guru/peers.go.
-func chanOps(instr ssa.Instruction) []chanOp {
-	fn := instr.Parent()
-	var ops []chanOp
-	switch instr := instr.(type) {
-	case *ssa.UnOp:
-		if instr.Op == token.ARROW {
-			// TODO(adonovan): don't assume <-ch; could be 'range ch'.
-			ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
-		}
-	case *ssa.Send:
-		ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
-	case *ssa.Select:
-		for _, st := range instr.States {
-			mode := "received"
-			if st.Dir == types.SendOnly {
-				mode = "sent"
-			}
-			ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
-		}
-	case ssa.CallInstruction:
-		call := instr.Common()
-		if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
-			pos := instr.Common().Pos()
-			ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
-		}
-	}
-	return ops
-}
diff --git a/internal/godoc/analysis/typeinfo.go b/internal/godoc/analysis/typeinfo.go
deleted file mode 100644
index e57683f..0000000
--- a/internal/godoc/analysis/typeinfo.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// 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
-}
diff --git a/internal/godoc/corpus.go b/internal/godoc/corpus.go
index 7bdb9aa..153e132 100644
--- a/internal/godoc/corpus.go
+++ b/internal/godoc/corpus.go
@@ -9,7 +9,6 @@
 	"sync"
 	"time"
 
-	"golang.org/x/website/internal/godoc/analysis"
 	"golang.org/x/website/internal/godoc/util"
 	"golang.org/x/website/internal/godoc/vfs"
 )
@@ -99,9 +98,6 @@
 	// SearchIndex is the search index in use.
 	searchIndex util.RWValue
 
-	// Analysis is the result of type and pointer analysis.
-	Analysis analysis.Result
-
 	// flag to check whether a corpus is initialized or not
 	initMu   sync.RWMutex
 	initDone bool
diff --git a/internal/godoc/godoc.go b/internal/godoc/godoc.go
index e981a1c..2a0c05c 100644
--- a/internal/godoc/godoc.go
+++ b/internal/godoc/godoc.go
@@ -18,7 +18,6 @@
 	"go/format"
 	"go/printer"
 	"go/token"
-	htmltemplate "html/template"
 	"io"
 	"log"
 	"os"
@@ -95,11 +94,6 @@
 		"example_name":   p.example_nameFunc,
 		"example_suffix": p.example_suffixFunc,
 
-		// formatting of analysis information
-		"callgraph_html":  p.callgraph_htmlFunc,
-		"implements_html": p.implements_htmlFunc,
-		"methodset_html":  p.methodset_htmlFunc,
-
 		// formatting of Notes
 		"noteTitle": noteTitle,
 
@@ -413,12 +407,6 @@
 	IsMain     bool                   // true for package main
 	IsFiltered bool                   // true if results were filtered
 
-	// analysis info
-	TypeInfoIndex  map[string]int  // index of JSON datum for type T (if -analysis=type)
-	AnalysisData   htmltemplate.JS // array of TypeInfoJSON values
-	CallGraph      htmltemplate.JS // array of PCGNodeJSON values    (if -analysis=pointer)
-	CallGraphIndex map[string]int  // maps func name to index in CallGraph
-
 	// directory info
 	Dirs    *DirList  // nil if no directory information
 	DirTime time.Time // directory time stamp
@@ -660,64 +648,6 @@
 	return suffix
 }
 
-// implements_html returns the "> Implements" toggle for a package-level named type.
-// Its contents are populated from JSON data by client-side JS at load time.
-func (p *Presentation) implements_htmlFunc(info *PageInfo, typeName string) string {
-	if p.ImplementsHTML == nil {
-		return ""
-	}
-	index, ok := info.TypeInfoIndex[typeName]
-	if !ok {
-		return ""
-	}
-	var buf bytes.Buffer
-	err := p.ImplementsHTML.Execute(&buf, struct{ Index int }{index})
-	if err != nil {
-		log.Print(err)
-	}
-	return buf.String()
-}
-
-// methodset_html returns the "> Method set" toggle for a package-level named type.
-// Its contents are populated from JSON data by client-side JS at load time.
-func (p *Presentation) methodset_htmlFunc(info *PageInfo, typeName string) string {
-	if p.MethodSetHTML == nil {
-		return ""
-	}
-	index, ok := info.TypeInfoIndex[typeName]
-	if !ok {
-		return ""
-	}
-	var buf bytes.Buffer
-	err := p.MethodSetHTML.Execute(&buf, struct{ Index int }{index})
-	if err != nil {
-		log.Print(err)
-	}
-	return buf.String()
-}
-
-// callgraph_html returns the "> Call graph" toggle for a package-level func.
-// Its contents are populated from JSON data by client-side JS at load time.
-func (p *Presentation) callgraph_htmlFunc(info *PageInfo, recv, name string) string {
-	if p.CallGraphHTML == nil {
-		return ""
-	}
-	if recv != "" {
-		// Format must match (*ssa.Function).RelString().
-		name = fmt.Sprintf("(%s).%s", recv, name)
-	}
-	index, ok := info.CallGraphIndex[name]
-	if !ok {
-		return ""
-	}
-	var buf bytes.Buffer
-	err := p.CallGraphHTML.Execute(&buf, struct{ Index int }{index})
-	if err != nil {
-		log.Print(err)
-	}
-	return buf.String()
-}
-
 func noteTitle(note string) string {
 	return strings.Title(strings.ToLower(note))
 }
diff --git a/internal/godoc/page.go b/internal/godoc/page.go
index 5833a89..a892a5a 100644
--- a/internal/godoc/page.go
+++ b/internal/godoc/page.go
@@ -23,7 +23,6 @@
 	Query    string
 	Body     []byte
 	GoogleCN bool // page is being served from golang.google.cn
-	TreeView bool // page needs to contain treeview related js and css
 
 	// filled in by ServePage
 	SearchBox       bool
diff --git a/internal/godoc/pres.go b/internal/godoc/pres.go
index fb17a42..bb0057b 100644
--- a/internal/godoc/pres.go
+++ b/internal/godoc/pres.go
@@ -25,13 +25,10 @@
 	cmdHandler handlerServer
 	pkgHandler handlerServer
 
-	CallGraphHTML,
 	DirlistHTML,
 	ErrorHTML,
 	ExampleHTML,
 	GodocHTML,
-	ImplementsHTML,
-	MethodSetHTML,
 	PackageHTML,
 	PackageRootHTML,
 	SearchHTML,
diff --git a/internal/godoc/server.go b/internal/godoc/server.go
index a8e0e8d..52178ed 100644
--- a/internal/godoc/server.go
+++ b/internal/godoc/server.go
@@ -14,7 +14,6 @@
 	"go/doc"
 	"go/token"
 	htmlpkg "html"
-	htmltemplate "html/template"
 	"io"
 	"io/ioutil"
 	"log"
@@ -27,7 +26,6 @@
 	"text/template"
 	"time"
 
-	"golang.org/x/website/internal/godoc/analysis"
 	"golang.org/x/website/internal/godoc/util"
 	"golang.org/x/website/internal/godoc/vfs"
 )
@@ -317,17 +315,6 @@
 		tabtitle = "Commands"
 	}
 
-	// Emit JSON array for type information.
-	pi := h.c.Analysis.PackageInfo(relpath)
-	hasTreeView := len(pi.CallGraph) != 0
-	info.CallGraphIndex = pi.CallGraphIndex
-	info.CallGraph = htmltemplate.JS(marshalJSON(pi.CallGraph))
-	info.AnalysisData = htmltemplate.JS(marshalJSON(pi.Types))
-	info.TypeInfoIndex = make(map[string]int)
-	for i, ti := range pi.Types {
-		info.TypeInfoIndex[ti.Name] = i
-	}
-
 	info.GoogleCN = googleCN(r)
 	var body []byte
 	if info.Dirname == "/src" {
@@ -341,7 +328,6 @@
 		Subtitle: subtitle,
 		Body:     body,
 		GoogleCN: info.GoogleCN,
-		TreeView: hasTreeView,
 	})
 }
 
@@ -583,20 +569,8 @@
 
 	var buf bytes.Buffer
 	if pathpkg.Ext(abspath) == ".go" {
-		// Find markup links for this file (e.g. "/src/fmt/print.go").
-		fi := p.Corpus.Analysis.FileInfo(abspath)
-		buf.WriteString("<script type='text/javascript'>document.ANALYSIS_DATA = ")
-		buf.Write(marshalJSON(fi.Data))
-		buf.WriteString(";</script>\n")
-
-		if status := p.Corpus.Analysis.Status(); status != "" {
-			buf.WriteString("<a href='/lib/godoc/analysis/help.html'>Static analysis features</a> ")
-			// TODO(adonovan): show analysis status at per-file granularity.
-			fmt.Fprintf(&buf, "<span style='color: grey'>[%s]</span><br/>", htmlpkg.EscapeString(status))
-		}
-
 		buf.WriteString("<pre>")
-		formatGoSource(&buf, src, fi.Links, h, s)
+		formatGoSource(&buf, src, h, s)
 		buf.WriteString("</pre>")
 	} else {
 		buf.WriteString("<pre>")
@@ -614,34 +588,18 @@
 	})
 }
 
-// formatGoSource HTML-escapes Go source text and writes it to w,
-// decorating it with the specified analysis links.
-//
-func formatGoSource(buf *bytes.Buffer, text []byte, links []analysis.Link, pattern string, selection Selection) {
+// formatGoSource HTML-escapes Go source text and writes it to w.
+func formatGoSource(buf *bytes.Buffer, text []byte, pattern string, selection Selection) {
 	// Emit to a temp buffer so that we can add line anchors at the end.
 	saved, buf := buf, new(bytes.Buffer)
 
-	var i int
-	var link analysis.Link // shared state of the two funcs below
-	segmentIter := func() (seg Segment) {
-		if i < len(links) {
-			link = links[i]
-			i++
-			seg = Segment{link.Start(), link.End()}
-		}
-		return
-	}
-	linkWriter := func(w io.Writer, offs int, start bool) {
-		link.Write(w, offs, start)
-	}
-
 	comments := tokenSelection(text, token.COMMENT)
 	var highlights Selection
 	if pattern != "" {
 		highlights = regexpSelection(text, pattern)
 	}
 
-	FormatSelections(buf, text, linkWriter, segmentIter, selectionTag, comments, highlights, selection)
+	FormatSelections(buf, text, nil, nil, selectionTag, comments, highlights, selection)
 
 	// Now copy buf to saved, adding line anchors.