1// Copyright 2019, The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE.md file. 4 5package cmp 6 7import ( 8 "fmt" 9 "reflect" 10 "strconv" 11 "strings" 12 "unicode" 13 14 "github.com/google/go-cmp/cmp/internal/flags" 15 "github.com/google/go-cmp/cmp/internal/value" 16) 17 18type formatValueOptions struct { 19 // AvoidStringer controls whether to avoid calling custom stringer 20 // methods like error.Error or fmt.Stringer.String. 21 AvoidStringer bool 22 23 // ShallowPointers controls whether to avoid descending into pointers. 24 // Useful when printing map keys, where pointer comparison is performed 25 // on the pointer address rather than the pointed-at value. 26 ShallowPointers bool 27 28 // PrintAddresses controls whether to print the address of all pointers, 29 // slice elements, and maps. 30 PrintAddresses bool 31} 32 33// FormatType prints the type as if it were wrapping s. 34// This may return s as-is depending on the current type and TypeMode mode. 35func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { 36 // Check whether to emit the type or not. 37 switch opts.TypeMode { 38 case autoType: 39 switch t.Kind() { 40 case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: 41 if s.Equal(textNil) { 42 return s 43 } 44 default: 45 return s 46 } 47 case elideType: 48 return s 49 } 50 51 // Determine the type label, applying special handling for unnamed types. 52 typeName := t.String() 53 if t.Name() == "" { 54 // According to Go grammar, certain type literals contain symbols that 55 // do not strongly bind to the next lexicographical token (e.g., *T). 56 switch t.Kind() { 57 case reflect.Chan, reflect.Func, reflect.Ptr: 58 typeName = "(" + typeName + ")" 59 } 60 typeName = strings.Replace(typeName, "struct {", "struct{", -1) 61 typeName = strings.Replace(typeName, "interface {", "interface{", -1) 62 } 63 64 // Avoid wrap the value in parenthesis if unnecessary. 65 if s, ok := s.(textWrap); ok { 66 hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")") 67 hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}") 68 if hasParens || hasBraces { 69 return textWrap{typeName, s, ""} 70 } 71 } 72 return textWrap{typeName + "(", s, ")"} 73} 74 75// FormatValue prints the reflect.Value, taking extra care to avoid descending 76// into pointers already in m. As pointers are visited, m is also updated. 77func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) { 78 if !v.IsValid() { 79 return nil 80 } 81 t := v.Type() 82 83 // Check whether there is an Error or String method to call. 84 if !opts.AvoidStringer && v.CanInterface() { 85 // Avoid calling Error or String methods on nil receivers since many 86 // implementations crash when doing so. 87 if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { 88 switch v := v.Interface().(type) { 89 case error: 90 return textLine("e" + formatString(v.Error())) 91 case fmt.Stringer: 92 return textLine("s" + formatString(v.String())) 93 } 94 } 95 } 96 97 // Check whether to explicitly wrap the result with the type. 98 var skipType bool 99 defer func() { 100 if !skipType { 101 out = opts.FormatType(t, out) 102 } 103 }() 104 105 var ptr string 106 switch t.Kind() { 107 case reflect.Bool: 108 return textLine(fmt.Sprint(v.Bool())) 109 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 110 return textLine(fmt.Sprint(v.Int())) 111 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 112 // Unnamed uints are usually bytes or words, so use hexadecimal. 113 if t.PkgPath() == "" || t.Kind() == reflect.Uintptr { 114 return textLine(formatHex(v.Uint())) 115 } 116 return textLine(fmt.Sprint(v.Uint())) 117 case reflect.Float32, reflect.Float64: 118 return textLine(fmt.Sprint(v.Float())) 119 case reflect.Complex64, reflect.Complex128: 120 return textLine(fmt.Sprint(v.Complex())) 121 case reflect.String: 122 return textLine(formatString(v.String())) 123 case reflect.UnsafePointer, reflect.Chan, reflect.Func: 124 return textLine(formatPointer(v)) 125 case reflect.Struct: 126 var list textList 127 for i := 0; i < v.NumField(); i++ { 128 vv := v.Field(i) 129 if value.IsZero(vv) { 130 continue // Elide fields with zero values 131 } 132 s := opts.WithTypeMode(autoType).FormatValue(vv, m) 133 list = append(list, textRecord{Key: t.Field(i).Name, Value: s}) 134 } 135 return textWrap{"{", list, "}"} 136 case reflect.Slice: 137 if v.IsNil() { 138 return textNil 139 } 140 if opts.PrintAddresses { 141 ptr = formatPointer(v) 142 } 143 fallthrough 144 case reflect.Array: 145 var list textList 146 for i := 0; i < v.Len(); i++ { 147 vi := v.Index(i) 148 if vi.CanAddr() { // Check for cyclic elements 149 p := vi.Addr() 150 if m.Visit(p) { 151 var out textNode 152 out = textLine(formatPointer(p)) 153 out = opts.WithTypeMode(emitType).FormatType(p.Type(), out) 154 out = textWrap{"*", out, ""} 155 list = append(list, textRecord{Value: out}) 156 continue 157 } 158 } 159 s := opts.WithTypeMode(elideType).FormatValue(vi, m) 160 list = append(list, textRecord{Value: s}) 161 } 162 return textWrap{ptr + "{", list, "}"} 163 case reflect.Map: 164 if v.IsNil() { 165 return textNil 166 } 167 if m.Visit(v) { 168 return textLine(formatPointer(v)) 169 } 170 171 var list textList 172 for _, k := range value.SortKeys(v.MapKeys()) { 173 sk := formatMapKey(k) 174 sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m) 175 list = append(list, textRecord{Key: sk, Value: sv}) 176 } 177 if opts.PrintAddresses { 178 ptr = formatPointer(v) 179 } 180 return textWrap{ptr + "{", list, "}"} 181 case reflect.Ptr: 182 if v.IsNil() { 183 return textNil 184 } 185 if m.Visit(v) || opts.ShallowPointers { 186 return textLine(formatPointer(v)) 187 } 188 if opts.PrintAddresses { 189 ptr = formatPointer(v) 190 } 191 skipType = true // Let the underlying value print the type instead 192 return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""} 193 case reflect.Interface: 194 if v.IsNil() { 195 return textNil 196 } 197 // Interfaces accept different concrete types, 198 // so configure the underlying value to explicitly print the type. 199 skipType = true // Print the concrete type instead 200 return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m) 201 default: 202 panic(fmt.Sprintf("%v kind not handled", v.Kind())) 203 } 204} 205 206// formatMapKey formats v as if it were a map key. 207// The result is guaranteed to be a single line. 208func formatMapKey(v reflect.Value) string { 209 var opts formatOptions 210 opts.TypeMode = elideType 211 opts.AvoidStringer = true 212 opts.ShallowPointers = true 213 s := opts.FormatValue(v, visitedPointers{}).String() 214 return strings.TrimSpace(s) 215} 216 217// formatString prints s as a double-quoted or backtick-quoted string. 218func formatString(s string) string { 219 // Use quoted string if it the same length as a raw string literal. 220 // Otherwise, attempt to use the raw string form. 221 qs := strconv.Quote(s) 222 if len(qs) == 1+len(s)+1 { 223 return qs 224 } 225 226 // Disallow newlines to ensure output is a single line. 227 // Only allow printable runes for readability purposes. 228 rawInvalid := func(r rune) bool { 229 return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') 230 } 231 if strings.IndexFunc(s, rawInvalid) < 0 { 232 return "`" + s + "`" 233 } 234 return qs 235} 236 237// formatHex prints u as a hexadecimal integer in Go notation. 238func formatHex(u uint64) string { 239 var f string 240 switch { 241 case u <= 0xff: 242 f = "0x%02x" 243 case u <= 0xffff: 244 f = "0x%04x" 245 case u <= 0xffffff: 246 f = "0x%06x" 247 case u <= 0xffffffff: 248 f = "0x%08x" 249 case u <= 0xffffffffff: 250 f = "0x%010x" 251 case u <= 0xffffffffffff: 252 f = "0x%012x" 253 case u <= 0xffffffffffffff: 254 f = "0x%014x" 255 case u <= 0xffffffffffffffff: 256 f = "0x%016x" 257 } 258 return fmt.Sprintf(f, u) 259} 260 261// formatPointer prints the address of the pointer. 262func formatPointer(v reflect.Value) string { 263 p := v.Pointer() 264 if flags.Deterministic { 265 p = 0xdeadf00f // Only used for stable testing purposes 266 } 267 return fmt.Sprintf("⟪0x%x⟫", p) 268} 269 270type visitedPointers map[value.Pointer]struct{} 271 272// Visit inserts pointer v into the visited map and reports whether it had 273// already been visited before. 274func (m visitedPointers) Visit(v reflect.Value) bool { 275 p := value.PointerOf(v) 276 _, visited := m[p] 277 m[p] = struct{}{} 278 return visited 279} 280