blob: 135479053d4600f1b8ad0501a73c138c3a43636f [file] [log] [blame] [edit]
// 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
}