func (v *trimVisitor) replaceStmt(stmt ast.Stmt) []ast.Stmt {
	switch stmt := stmt.(type) {
	case nil:
		return nil
	default:
		return []ast.Stmt{stmt}
	case *ast.RangeStmt:
		if v.visited(stmt.Body) {
			return []ast.Stmt{stmt}
		}
		call := v.findCall(stmt.X)
		if call != nil {
			return []ast.Stmt{&ast.ExprStmt{call}}
		}
		return nil
	case *ast.ForStmt:
		if v.visited(stmt.Body) {
			return []ast.Stmt{stmt}
		}
		nodes := []*ast.CallExpr{
			v.findCall(stmt.Init),
			v.findCall(stmt.Cond),
			v.findCall(stmt.Post),
		}
		var result []ast.Stmt
		for _, call := range nodes {
			if call != nil {
				result = append(result, &ast.ExprStmt{call})
			}
		}
		return result
	case *ast.IfStmt:
		vIf := v.visited(stmt.Body)
		vElse := v.visited(stmt.Else)
		if !vIf {
			var result []ast.Stmt
			nodes := []*ast.CallExpr{
				v.findCall(stmt.Init),
				v.findCall(stmt.Cond),
			}
			for _, call := range nodes {
				if call != nil {
					result = append(result, &ast.ExprStmt{call})
				}
			}
			if vElse {
				if block, ok := stmt.Else.(*ast.BlockStmt); ok {
					for _, stmt := range block.List {
						result = append(result, v.replaceStmt(stmt)...)
					}
				} else {
					result = append(result, v.replaceStmt(stmt.Else)...)
				}
			}
			return result
		} else {
			if !vElse {
				stmt.Else = nil
			}
			return []ast.Stmt{stmt}
		}
	case *ast.SelectStmt:
		var list []ast.Stmt
		for _, stmt := range stmt.Body.List {
			if v.visited(stmt) {
				list = append(list, stmt)
			}
		}
		stmt.Body.List = list
		return []ast.Stmt{stmt}
	case *ast.SwitchStmt:
		var list []ast.Stmt
		for _, stmt := range stmt.Body.List {
			if v.visitedAndMatters(stmt) {
				list = append(list, stmt)
			}
		}
		if len(list) == 0 {
			return nil
		} else {
			stmt.Body.List = list
			return []ast.Stmt{stmt}
		}
	case *ast.TypeSwitchStmt:
		var list []ast.Stmt
		for _, stmt := range stmt.Body.List {
			if v.visitedAndMatters(stmt) {
				list = append(list, stmt)
			}
		}
		if len(list) == 0 {
			return nil
		} else {
			stmt.Body.List = list
			return []ast.Stmt{stmt}
		}
	}
}
replaceStmt returns the (possibly many) statements that should replace
stmt. Generally a stmt is untouched or removed, but in some cases a
single stmt can result in multiple statements. This is usually only the case
when removing a block that was not taken, but pulling out function calls
that were part of the initialization of the block.
package discover
import "go/ast"
func (p *Profile) Trim(node ast.Node) {
	if f, ok := node.(*ast.File); ok {
		cmap := ast.NewCommentMap(p.Fset, f, f.Comments)
		ast.Walk(&trimVisitor{p}, f)
		f.Comments = cmap.Filter(f).Comments()
	} else {
		ast.Walk(&trimVisitor{p}, node)
	}
}
type trimVisitor struct {
	p *Profile
}
func (v *trimVisitor) Visit(node ast.Node) ast.Visitor {
	var list *[]ast.Stmt
	switch node := node.(type) {
	case *ast.File:
		var replaced []ast.Decl
		for _, decl := range node.Decls {
			if f, ok := decl.(*ast.FuncDecl); ok && v.p.Funcs[f] {
				replaced = append(replaced, decl)
			}
		}
		node.Decls = replaced
	case *ast.BlockStmt:
		list = &node.List
	case *ast.CommClause:
		list = &node.Body
	case *ast.CaseClause:
		list = &node.Body
	}
	if list != nil {
		var replaced []ast.Stmt
		for _, stmt := range *list {
			replaced = append(replaced, v.replaceStmt(stmt)...)
		}
		*list = replaced
	}
	return v
}