|  | // TODO: show that two-non-empty dotjoin can happen, by using an anon struct as a field type | 
|  | // TODO: don't report removed/changed methods for both value and pointer method sets? | 
|  |  | 
|  | package apidiff | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "go/types" | 
|  | "sort" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // There can be at most one message for each object or part thereof. | 
|  | // Parts include interface methods and struct fields. | 
|  | // | 
|  | // The part thing is necessary. Method (Func) objects have sufficient info, but field | 
|  | // Vars do not: they just have a field name and a type, without the enclosing struct. | 
|  | type messageSet map[types.Object]map[string]string | 
|  |  | 
|  | // Add a message for obj and part, overwriting a previous message | 
|  | // (shouldn't happen). | 
|  | // obj is required but part can be empty. | 
|  | func (m messageSet) add(obj types.Object, part, msg string) { | 
|  | s := m[obj] | 
|  | if s == nil { | 
|  | s = map[string]string{} | 
|  | m[obj] = s | 
|  | } | 
|  | if f, ok := s[part]; ok && f != msg { | 
|  | fmt.Printf("! second, different message for obj %s, part %q\n", obj, part) | 
|  | fmt.Printf("  first:  %s\n", f) | 
|  | fmt.Printf("  second: %s\n", msg) | 
|  | } | 
|  | s[part] = msg | 
|  | } | 
|  |  | 
|  | func (m messageSet) collect() []string { | 
|  | var s []string | 
|  | for obj, parts := range m { | 
|  | // Format each object name relative to its own package. | 
|  | objstring := objectString(obj) | 
|  | for part, msg := range parts { | 
|  | var p string | 
|  |  | 
|  | if strings.HasPrefix(part, ",") { | 
|  | p = objstring + part | 
|  | } else { | 
|  | p = dotjoin(objstring, part) | 
|  | } | 
|  | s = append(s, p+": "+msg) | 
|  | } | 
|  | } | 
|  | sort.Strings(s) | 
|  | return s | 
|  | } | 
|  |  | 
|  | func objectString(obj types.Object) string { | 
|  | if f, ok := obj.(*types.Func); ok { | 
|  | sig := f.Type().(*types.Signature) | 
|  | if recv := sig.Recv(); recv != nil { | 
|  | tn := types.TypeString(recv.Type(), types.RelativeTo(obj.Pkg())) | 
|  | if tn[0] == '*' { | 
|  | tn = "(" + tn + ")" | 
|  | } | 
|  | return fmt.Sprintf("%s.%s", tn, obj.Name()) | 
|  | } | 
|  | } | 
|  | return obj.Name() | 
|  | } | 
|  |  | 
|  | func dotjoin(s1, s2 string) string { | 
|  | if s1 == "" { | 
|  | return s2 | 
|  | } | 
|  | if s2 == "" { | 
|  | return s1 | 
|  | } | 
|  | return s1 + "." + s2 | 
|  | } |