marshal.go (9235B)
1 // Copyright 2011 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 file. 4 5 // This file contains a modified copy of the encoding/xml encoder. 6 // All dynamic behavior has been removed, and reflecttion has been replaced with go/types. 7 // This allows us to statically find unmarshable types 8 // with the same rules for tags, shadowing and addressability as encoding/xml. 9 // This is used for SA1026 and SA5008. 10 11 // NOTE(dh): we do not check CanInterface in various places, which means we'll accept more marshaler implementations than encoding/xml does. This will lead to a small amount of false negatives. 12 13 package fakexml 14 15 import ( 16 "fmt" 17 "go/types" 18 "strings" 19 20 "honnef.co/go/tools/go/types/typeutil" 21 "honnef.co/go/tools/knowledge" 22 "honnef.co/go/tools/staticcheck/fakereflect" 23 ) 24 25 func Marshal(v types.Type) error { 26 return NewEncoder().Encode(v) 27 } 28 29 type Encoder struct { 30 // TODO we track addressable and non-addressable instances separately out of an abundance of caution. We don't know 31 // if this is actually required for correctness. 32 seenCanAddr typeutil.Map[struct{}] 33 seenCantAddr typeutil.Map[struct{}] 34 } 35 36 func NewEncoder() *Encoder { 37 e := &Encoder{} 38 return e 39 } 40 41 func (enc *Encoder) Encode(v types.Type) error { 42 rv := fakereflect.TypeAndCanAddr{Type: v} 43 return enc.marshalValue(rv, nil, nil, "x") 44 } 45 46 func implementsMarshaler(v fakereflect.TypeAndCanAddr) bool { 47 t := v.Type 48 obj, _, _ := types.LookupFieldOrMethod(t, false, nil, "MarshalXML") 49 if obj == nil { 50 return false 51 } 52 fn, ok := obj.(*types.Func) 53 if !ok { 54 return false 55 } 56 params := fn.Type().(*types.Signature).Params() 57 if params.Len() != 2 { 58 return false 59 } 60 if !typeutil.IsPointerToTypeWithName(params.At(0).Type(), "encoding/xml.Encoder") { 61 return false 62 } 63 if !typeutil.IsTypeWithName(params.At(1).Type(), "encoding/xml.StartElement") { 64 return false 65 } 66 rets := fn.Type().(*types.Signature).Results() 67 if rets.Len() != 1 { 68 return false 69 } 70 if !typeutil.IsTypeWithName(rets.At(0).Type(), "error") { 71 return false 72 } 73 return true 74 } 75 76 func implementsMarshalerAttr(v fakereflect.TypeAndCanAddr) bool { 77 t := v.Type 78 obj, _, _ := types.LookupFieldOrMethod(t, false, nil, "MarshalXMLAttr") 79 if obj == nil { 80 return false 81 } 82 fn, ok := obj.(*types.Func) 83 if !ok { 84 return false 85 } 86 params := fn.Type().(*types.Signature).Params() 87 if params.Len() != 1 { 88 return false 89 } 90 if !typeutil.IsTypeWithName(params.At(0).Type(), "encoding/xml.Name") { 91 return false 92 } 93 rets := fn.Type().(*types.Signature).Results() 94 if rets.Len() != 2 { 95 return false 96 } 97 if !typeutil.IsTypeWithName(rets.At(0).Type(), "encoding/xml.Attr") { 98 return false 99 } 100 if !typeutil.IsTypeWithName(rets.At(1).Type(), "error") { 101 return false 102 } 103 return true 104 } 105 106 type CyclicTypeError struct { 107 Type types.Type 108 Path string 109 } 110 111 func (err *CyclicTypeError) Error() string { 112 return "cyclic type" 113 } 114 115 // marshalValue writes one or more XML elements representing val. 116 // If val was obtained from a struct field, finfo must have its details. 117 func (e *Encoder) marshalValue(val fakereflect.TypeAndCanAddr, finfo *fieldInfo, startTemplate *StartElement, stack string) error { 118 var m *typeutil.Map[struct{}] 119 if val.CanAddr() { 120 m = &e.seenCanAddr 121 } else { 122 m = &e.seenCantAddr 123 } 124 if _, ok := m.At(val.Type); ok { 125 return nil 126 } 127 m.Set(val.Type, struct{}{}) 128 129 // Drill into interfaces and pointers. 130 seen := map[fakereflect.TypeAndCanAddr]struct{}{} 131 for val.IsInterface() || val.IsPtr() { 132 if val.IsInterface() { 133 return nil 134 } 135 val = val.Elem() 136 if _, ok := seen[val]; ok { 137 // Loop in type graph, e.g. 'type P *P' 138 return &CyclicTypeError{val.Type, stack} 139 } 140 seen[val] = struct{}{} 141 } 142 143 // Check for marshaler. 144 if implementsMarshaler(val) { 145 return nil 146 } 147 if val.CanAddr() { 148 pv := fakereflect.PtrTo(val) 149 if implementsMarshaler(pv) { 150 return nil 151 } 152 } 153 154 // Check for text marshaler. 155 if val.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 156 return nil 157 } 158 if val.CanAddr() { 159 pv := fakereflect.PtrTo(val) 160 if pv.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 161 return nil 162 } 163 } 164 165 // Slices and arrays iterate over the elements. They do not have an enclosing tag. 166 if (val.IsSlice() || val.IsArray()) && !isByteArray(val) && !isByteSlice(val) { 167 if err := e.marshalValue(val.Elem(), finfo, startTemplate, stack+"[0]"); err != nil { 168 return err 169 } 170 return nil 171 } 172 173 tinfo, err := getTypeInfo(val) 174 if err != nil { 175 return err 176 } 177 178 // Create start element. 179 // Precedence for the XML element name is: 180 // 0. startTemplate 181 // 1. XMLName field in underlying struct; 182 // 2. field name/tag in the struct field; and 183 // 3. type name 184 var start StartElement 185 186 if startTemplate != nil { 187 start.Name = startTemplate.Name 188 start.Attr = append(start.Attr, startTemplate.Attr...) 189 } else if tinfo.xmlname != nil { 190 xmlname := tinfo.xmlname 191 if xmlname.name != "" { 192 start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name 193 } 194 } 195 196 // Attributes 197 for i := range tinfo.fields { 198 finfo := &tinfo.fields[i] 199 if finfo.flags&fAttr == 0 { 200 continue 201 } 202 fv := finfo.value(val) 203 204 name := Name{Space: finfo.xmlns, Local: finfo.name} 205 if err := e.marshalAttr(&start, name, fv, stack+pathByIndex(val, finfo.idx)); err != nil { 206 return err 207 } 208 } 209 210 if val.IsStruct() { 211 return e.marshalStruct(tinfo, val, stack) 212 } else { 213 return e.marshalSimple(val, stack) 214 } 215 } 216 217 func isSlice(v fakereflect.TypeAndCanAddr) bool { 218 _, ok := v.Type.Underlying().(*types.Slice) 219 return ok 220 } 221 222 func isByteSlice(v fakereflect.TypeAndCanAddr) bool { 223 slice, ok := v.Type.Underlying().(*types.Slice) 224 if !ok { 225 return false 226 } 227 basic, ok := slice.Elem().Underlying().(*types.Basic) 228 if !ok { 229 return false 230 } 231 return basic.Kind() == types.Uint8 232 } 233 234 func isByteArray(v fakereflect.TypeAndCanAddr) bool { 235 slice, ok := v.Type.Underlying().(*types.Array) 236 if !ok { 237 return false 238 } 239 basic, ok := slice.Elem().Underlying().(*types.Basic) 240 if !ok { 241 return false 242 } 243 return basic.Kind() == types.Uint8 244 } 245 246 // marshalAttr marshals an attribute with the given name and value, adding to start.Attr. 247 func (e *Encoder) marshalAttr(start *StartElement, name Name, val fakereflect.TypeAndCanAddr, stack string) error { 248 if implementsMarshalerAttr(val) { 249 return nil 250 } 251 252 if val.CanAddr() { 253 pv := fakereflect.PtrTo(val) 254 if implementsMarshalerAttr(pv) { 255 return nil 256 } 257 } 258 259 if val.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 260 return nil 261 } 262 263 if val.CanAddr() { 264 pv := fakereflect.PtrTo(val) 265 if pv.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 266 return nil 267 } 268 } 269 270 // Dereference or skip nil pointer 271 if val.IsPtr() { 272 val = val.Elem() 273 } 274 275 // Walk slices. 276 if isSlice(val) && !isByteSlice(val) { 277 if err := e.marshalAttr(start, name, val.Elem(), stack+"[0]"); err != nil { 278 return err 279 } 280 return nil 281 } 282 283 if typeutil.IsTypeWithName(val.Type, "encoding/xml.Attr") { 284 return nil 285 } 286 287 return e.marshalSimple(val, stack) 288 } 289 290 func (e *Encoder) marshalSimple(val fakereflect.TypeAndCanAddr, stack string) error { 291 switch val.Type.Underlying().(type) { 292 case *types.Basic, *types.Interface: 293 return nil 294 case *types.Slice, *types.Array: 295 basic, ok := val.Elem().Type.Underlying().(*types.Basic) 296 if !ok || basic.Kind() != types.Uint8 { 297 return &UnsupportedTypeError{val.Type, stack} 298 } 299 return nil 300 default: 301 return &UnsupportedTypeError{val.Type, stack} 302 } 303 } 304 305 func indirect(vf fakereflect.TypeAndCanAddr) fakereflect.TypeAndCanAddr { 306 for vf.IsPtr() { 307 vf = vf.Elem() 308 } 309 return vf 310 } 311 312 func pathByIndex(t fakereflect.TypeAndCanAddr, index []int) string { 313 var path strings.Builder 314 for _, i := range index { 315 if t.IsPtr() { 316 t = t.Elem() 317 } 318 path.WriteString("." + t.Field(i).Name) 319 t = t.Field(i).Type 320 } 321 return path.String() 322 } 323 324 func (e *Encoder) marshalStruct(tinfo *typeInfo, val fakereflect.TypeAndCanAddr, stack string) error { 325 for i := range tinfo.fields { 326 finfo := &tinfo.fields[i] 327 if finfo.flags&fAttr != 0 { 328 continue 329 } 330 vf := finfo.value(val) 331 332 switch finfo.flags & fMode { 333 case fCDATA, fCharData: 334 if vf.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 335 continue 336 } 337 if vf.CanAddr() { 338 pv := fakereflect.PtrTo(vf) 339 if pv.Implements(knowledge.Interfaces["encoding.TextMarshaler"]) { 340 continue 341 } 342 } 343 continue 344 345 case fComment: 346 vf = indirect(vf) 347 if !(isByteSlice(vf) || isByteArray(vf)) { 348 return fmt.Errorf("xml: bad type for comment field of %s", val) 349 } 350 continue 351 352 case fInnerXML: 353 vf = indirect(vf) 354 if t, ok := vf.Type.(*types.Slice); (ok && types.Identical(t.Elem(), types.Typ[types.Byte])) || types.Identical(vf.Type, types.Typ[types.String]) { 355 continue 356 } 357 358 case fElement, fElement | fAny: 359 } 360 if err := e.marshalValue(vf, finfo, nil, stack+pathByIndex(val, finfo.idx)); err != nil { 361 return err 362 } 363 } 364 return nil 365 } 366 367 // UnsupportedTypeError is returned when Marshal encounters a type 368 // that cannot be converted into XML. 369 type UnsupportedTypeError struct { 370 Type types.Type 371 Path string 372 } 373 374 func (e *UnsupportedTypeError) Error() string { 375 return fmt.Sprintf("xml: unsupported type %s, via %s ", e.Type, e.Path) 376 }