func extractVars(fileData []byte) (extractedVars, error) {
	fs := token.NewFileSet()
	f, err := parser.ParseFile(fs, "", fileData, 0)
	if err != nil {
		return nil, err
	}
	vars := make(extractedVars)
	for _, dec := range f.Decls {
		switch dt := dec.(type) {
		case *ast.FuncDecl:
			structName := ""
			switch rt := dt.Recv.List[0].Type.(type) {
			case *ast.Ident:
				structName = rt.Name
			case *ast.StarExpr:
				structName = rt.X.(*ast.Ident).Name
			default:
				panic("unknown receiver type")
			}
			key := structName + "." + dt.Name.Name
			vis := &varVisitor{fset: fs}
			ast.Walk(vis, dt.Body)
			vars[key] = vis.vars
		}
	}
	return vars, nil
}
extractVars extracts all top-level declaration in fileData (representing Go source code) and adds
each to an extractedVars map, with the keys being the qualified struct method names.
package ident_names
import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"reflect"
	"sort"
	"testing"
	"github.com/dchenk/msgp/gen"
)
func TestIdentNames(t *testing.T) {
	testCases := []struct {
		newSrc           string
		expectedToChange []string
	}{  
		{"./structs_small_changed.gosrc", []string{"SmallStruct"}},
		{"./structs_big_changed.gosrc", []string{"BigStruct"}},
		{"./structs_both_changed.gosrc", []string{"SmallStruct", "BigStruct"}},
	}
	mode := gen.Decode | gen.Encode | gen.Size | gen.Marshal | gen.Unmarshal
	methods := []string{"DecodeMsg", "EncodeMsg", "Msgsize", "MarshalMsg", "UnmarshalMsg"}
	buf1, _, err := gen.RunData("./structs.go", mode, false)
	if err != nil {
		t.Fatalf("error running gen; %s", err)
	}
	for indx, testCase := range testCases {
		vars1, err := extractVars(buf1.Bytes())
		if err != nil {
			t.Fatalf("(indx %d): could not extract vars2: %v", indx, err)
		}
		buf2, _, err := gen.RunData(testCase.newSrc, mode, false)
		if err != nil {
			t.Fatalf("error running gen; %s", err)
		}
		vars2, err := extractVars(buf2.Bytes())
		if err != nil {
			t.Fatalf("(indx %d): could not extract vars2: %v", indx, err)
		}
		for _, structName := range testCase.expectedToChange {
			for _, methodType := range methods {
				methodName := structName + "." + methodType
				val1, val2 := vars1.Value(methodName), vars2.Value(methodName)
				if (len(val1) > 0 || len(val2) > 0) && reflect.DeepEqual(val1, val2) {
					t.Log("val1:", val1)
					t.Log("val2:", val2)
					t.Fatalf("(indx %d): vars identical but expected vars to change for %s", indx, methodName)
				}
				delete(vars1, methodName)
				delete(vars2, methodName)
			}
		}
		for methodName := range vars1 {
			if !reflect.DeepEqual(vars1[methodName], vars2.Value(methodName)) {
				t.Fatalf("%d: vars changed but expected identical vars for %s", indx, methodName)
			}
			delete(vars1, methodName)
			delete(vars2, methodName)
		}
		if len(vars1) > 0 || len(vars2) > 0 {
			t.Fatalf("(indx %d): unexpected methods remaining", indx)
		}
	}
}
func TestIdentNamesShadowing(t *testing.T) {
	srcs := []string{"./structs.go", "./structs_small_changed.gosrc", "./structs_big_changed.gosrc", "./structs_both_changed.gosrc"}
	mode := gen.Decode | gen.Encode | gen.Size | gen.Marshal | gen.Unmarshal
	for indx, src := range srcs {
		buf, _, err := gen.RunData(src, mode, false)
		if err != nil {
			t.Fatalf("error running gen; %s", err)
		}
		vars, err := extractVars(buf.Bytes())
		if err != nil {
			t.Fatalf("(indx %d): could not extract vars: %v", indx, err)
		}
		count := 0
		for methodName, methodVars := range vars {
			sort.Strings(methodVars)
			for _, v := range methodVars {
				if v[0] == 'z' {
					count++
				}
			}
			for i := range methodVars {
				for j := range methodVars {
					if methodVars[i] == methodVars[j] && i != j {
						t.Fatalf("(indx %d): duplicate var %s in function %s", indx, methodVars[i], methodName)
					}
				}
			}
		}
		if count == 0 {
			t.Fatalf("(indx %d): no generated identifiers found", indx)
		}
	}
}
type extractedVars map[string][]string
func (e extractedVars) Value(key string) []string {
	if v, ok := e[key]; ok {
		return v
	}
	panic(fmt.Errorf("unknown key %s", key))  
}