staticcheck (#313)
* CI: use staticcheck for linting This commit switches the linter for Go code from golint to staticcheck. Golint has been deprecated since last year and staticcheck is a recommended replacement. Signed-off-by: Lucas Servén Marín <lserven@gmail.com> * revendor Signed-off-by: Lucas Servén Marín <lserven@gmail.com> * cmd,pkg: fix lint warnings Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
committed by
GitHub
parent
93f46e03ea
commit
50fbc2eec2
380
vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go
vendored
Normal file
380
vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains a modified copy of the encoding/xml encoder.
|
||||
// All dynamic behavior has been removed, and reflecttion has been replaced with go/types.
|
||||
// This allows us to statically find unmarshable types
|
||||
// with the same rules for tags, shadowing and addressability as encoding/xml.
|
||||
// This is used for SA1026 and SA5008.
|
||||
|
||||
// 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.
|
||||
|
||||
package fakexml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/go/types/typeutil"
|
||||
"honnef.co/go/tools/staticcheck/fakereflect"
|
||||
)
|
||||
|
||||
func Marshal(v types.Type) error {
|
||||
return NewEncoder().Encode(v)
|
||||
}
|
||||
|
||||
type Encoder struct {
|
||||
seen map[fakereflect.TypeAndCanAddr]struct{}
|
||||
}
|
||||
|
||||
func NewEncoder() *Encoder {
|
||||
e := &Encoder{
|
||||
seen: map[fakereflect.TypeAndCanAddr]struct{}{},
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (enc *Encoder) Encode(v types.Type) error {
|
||||
rv := fakereflect.TypeAndCanAddr{Type: v}
|
||||
return enc.marshalValue(rv, nil, nil, "x")
|
||||
}
|
||||
|
||||
func implementsMarshaler(v fakereflect.TypeAndCanAddr) bool {
|
||||
t := v.Type
|
||||
obj, _, _ := types.LookupFieldOrMethod(t, false, nil, "MarshalXML")
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
fn, ok := obj.(*types.Func)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
params := fn.Type().(*types.Signature).Params()
|
||||
if params.Len() != 2 {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(params.At(0).Type(), "*encoding/xml.Encoder") {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(params.At(1).Type(), "encoding/xml.StartElement") {
|
||||
return false
|
||||
}
|
||||
rets := fn.Type().(*types.Signature).Results()
|
||||
if rets.Len() != 1 {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(rets.At(0).Type(), "error") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func implementsMarshalerAttr(v fakereflect.TypeAndCanAddr) bool {
|
||||
t := v.Type
|
||||
obj, _, _ := types.LookupFieldOrMethod(t, false, nil, "MarshalXMLAttr")
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
fn, ok := obj.(*types.Func)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
params := fn.Type().(*types.Signature).Params()
|
||||
if params.Len() != 1 {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(params.At(0).Type(), "encoding/xml.Name") {
|
||||
return false
|
||||
}
|
||||
rets := fn.Type().(*types.Signature).Results()
|
||||
if rets.Len() != 2 {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(rets.At(0).Type(), "encoding/xml.Attr") {
|
||||
return false
|
||||
}
|
||||
if !typeutil.IsType(rets.At(1).Type(), "error") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var textMarshalerType = types.NewInterfaceType([]*types.Func{
|
||||
types.NewFunc(token.NoPos, nil, "MarshalText", types.NewSignature(nil,
|
||||
types.NewTuple(),
|
||||
types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "", types.NewSlice(types.Typ[types.Byte])),
|
||||
types.NewVar(0, nil, "", types.Universe.Lookup("error").Type())),
|
||||
false,
|
||||
)),
|
||||
}, nil).Complete()
|
||||
|
||||
var N = 0
|
||||
|
||||
type CyclicTypeError struct {
|
||||
Type types.Type
|
||||
Path string
|
||||
}
|
||||
|
||||
func (err *CyclicTypeError) Error() string {
|
||||
return "cyclic type"
|
||||
}
|
||||
|
||||
// marshalValue writes one or more XML elements representing val.
|
||||
// If val was obtained from a struct field, finfo must have its details.
|
||||
func (e *Encoder) marshalValue(val fakereflect.TypeAndCanAddr, finfo *fieldInfo, startTemplate *StartElement, stack string) error {
|
||||
if _, ok := e.seen[val]; ok {
|
||||
return nil
|
||||
}
|
||||
e.seen[val] = struct{}{}
|
||||
|
||||
// Drill into interfaces and pointers.
|
||||
seen := map[fakereflect.TypeAndCanAddr]struct{}{}
|
||||
for val.IsInterface() || val.IsPtr() {
|
||||
if val.IsInterface() {
|
||||
return nil
|
||||
}
|
||||
val = val.Elem()
|
||||
if _, ok := seen[val]; ok {
|
||||
// Loop in type graph, e.g. 'type P *P'
|
||||
return &CyclicTypeError{val.Type, stack}
|
||||
}
|
||||
seen[val] = struct{}{}
|
||||
}
|
||||
|
||||
// Check for marshaler.
|
||||
if implementsMarshaler(val) {
|
||||
return nil
|
||||
}
|
||||
if val.CanAddr() {
|
||||
pv := fakereflect.PtrTo(val)
|
||||
if implementsMarshaler(pv) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check for text marshaler.
|
||||
if val.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
if val.CanAddr() {
|
||||
pv := fakereflect.PtrTo(val)
|
||||
if pv.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Slices and arrays iterate over the elements. They do not have an enclosing tag.
|
||||
if (val.IsSlice() || val.IsArray()) && !isByteArray(val) && !isByteSlice(val) {
|
||||
if err := e.marshalValue(val.Elem(), finfo, startTemplate, stack+"[0]"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tinfo, err := getTypeInfo(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create start element.
|
||||
// Precedence for the XML element name is:
|
||||
// 0. startTemplate
|
||||
// 1. XMLName field in underlying struct;
|
||||
// 2. field name/tag in the struct field; and
|
||||
// 3. type name
|
||||
var start StartElement
|
||||
|
||||
if startTemplate != nil {
|
||||
start.Name = startTemplate.Name
|
||||
start.Attr = append(start.Attr, startTemplate.Attr...)
|
||||
} else if tinfo.xmlname != nil {
|
||||
xmlname := tinfo.xmlname
|
||||
if xmlname.name != "" {
|
||||
start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes
|
||||
for i := range tinfo.fields {
|
||||
finfo := &tinfo.fields[i]
|
||||
if finfo.flags&fAttr == 0 {
|
||||
continue
|
||||
}
|
||||
fv := finfo.value(val)
|
||||
|
||||
name := Name{Space: finfo.xmlns, Local: finfo.name}
|
||||
if err := e.marshalAttr(&start, name, fv, stack+pathByIndex(val, finfo.idx)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if val.IsStruct() {
|
||||
return e.marshalStruct(tinfo, val, stack)
|
||||
} else {
|
||||
return e.marshalSimple(val, stack)
|
||||
}
|
||||
}
|
||||
|
||||
func isSlice(v fakereflect.TypeAndCanAddr) bool {
|
||||
_, ok := v.Type.Underlying().(*types.Slice)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isByteSlice(v fakereflect.TypeAndCanAddr) bool {
|
||||
slice, ok := v.Type.Underlying().(*types.Slice)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
basic, ok := slice.Elem().Underlying().(*types.Basic)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return basic.Kind() == types.Uint8
|
||||
}
|
||||
|
||||
func isByteArray(v fakereflect.TypeAndCanAddr) bool {
|
||||
slice, ok := v.Type.Underlying().(*types.Array)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
basic, ok := slice.Elem().Underlying().(*types.Basic)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return basic.Kind() == types.Uint8
|
||||
}
|
||||
|
||||
// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
|
||||
func (e *Encoder) marshalAttr(start *StartElement, name Name, val fakereflect.TypeAndCanAddr, stack string) error {
|
||||
if implementsMarshalerAttr(val) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if val.CanAddr() {
|
||||
pv := fakereflect.PtrTo(val)
|
||||
if implementsMarshalerAttr(pv) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if val.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if val.CanAddr() {
|
||||
pv := fakereflect.PtrTo(val)
|
||||
if pv.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Dereference or skip nil pointer
|
||||
if val.IsPtr() {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
// Walk slices.
|
||||
if isSlice(val) && !isByteSlice(val) {
|
||||
if err := e.marshalAttr(start, name, val.Elem(), stack+"[0]"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if typeutil.IsType(val.Type, "encoding/xml.Attr") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.marshalSimple(val, stack)
|
||||
}
|
||||
|
||||
func (e *Encoder) marshalSimple(val fakereflect.TypeAndCanAddr, stack string) error {
|
||||
switch val.Type.Underlying().(type) {
|
||||
case *types.Basic, *types.Interface:
|
||||
return nil
|
||||
case *types.Slice, *types.Array:
|
||||
basic, ok := val.Elem().Type.Underlying().(*types.Basic)
|
||||
if !ok || basic.Kind() != types.Uint8 {
|
||||
return &UnsupportedTypeError{val.Type, stack}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return &UnsupportedTypeError{val.Type, stack}
|
||||
}
|
||||
}
|
||||
|
||||
func indirect(vf fakereflect.TypeAndCanAddr) fakereflect.TypeAndCanAddr {
|
||||
for vf.IsPtr() {
|
||||
vf = vf.Elem()
|
||||
}
|
||||
return vf
|
||||
}
|
||||
|
||||
func pathByIndex(t fakereflect.TypeAndCanAddr, index []int) string {
|
||||
path := ""
|
||||
for _, i := range index {
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
path += "." + t.Field(i).Name
|
||||
t = t.Field(i).Type
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (e *Encoder) marshalStruct(tinfo *typeInfo, val fakereflect.TypeAndCanAddr, stack string) error {
|
||||
for i := range tinfo.fields {
|
||||
finfo := &tinfo.fields[i]
|
||||
if finfo.flags&fAttr != 0 {
|
||||
continue
|
||||
}
|
||||
vf := finfo.value(val)
|
||||
|
||||
switch finfo.flags & fMode {
|
||||
case fCDATA, fCharData:
|
||||
if vf.Implements(textMarshalerType) {
|
||||
continue
|
||||
}
|
||||
if vf.CanAddr() {
|
||||
pv := fakereflect.PtrTo(vf)
|
||||
if pv.Implements(textMarshalerType) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
|
||||
case fComment:
|
||||
vf = indirect(vf)
|
||||
if !(isByteSlice(vf) || isByteArray(vf)) {
|
||||
return fmt.Errorf("xml: bad type for comment field of %s", val)
|
||||
}
|
||||
continue
|
||||
|
||||
case fInnerXML:
|
||||
vf = indirect(vf)
|
||||
if typeutil.IsType(vf.Type, "[]byte") || typeutil.IsType(vf.Type, "string") {
|
||||
continue
|
||||
}
|
||||
|
||||
case fElement, fElement | fAny:
|
||||
}
|
||||
if err := e.marshalValue(vf, finfo, nil, stack+pathByIndex(val, finfo.idx)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsupportedTypeError is returned when Marshal encounters a type
|
||||
// that cannot be converted into XML.
|
||||
type UnsupportedTypeError struct {
|
||||
Type types.Type
|
||||
Path string
|
||||
}
|
||||
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
return fmt.Sprintf("xml: unsupported type %s, via %s ", e.Type, e.Path)
|
||||
}
|
389
vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go
vendored
Normal file
389
vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakexml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"honnef.co/go/tools/staticcheck/fakereflect"
|
||||
)
|
||||
|
||||
// typeInfo holds details for the xml representation of a type.
|
||||
type typeInfo struct {
|
||||
xmlname *fieldInfo
|
||||
fields []fieldInfo
|
||||
}
|
||||
|
||||
// fieldInfo holds details for the xml representation of a single field.
|
||||
type fieldInfo struct {
|
||||
idx []int
|
||||
name string
|
||||
xmlns string
|
||||
flags fieldFlags
|
||||
parents []string
|
||||
}
|
||||
|
||||
type fieldFlags int
|
||||
|
||||
const (
|
||||
fElement fieldFlags = 1 << iota
|
||||
fAttr
|
||||
fCDATA
|
||||
fCharData
|
||||
fInnerXML
|
||||
fComment
|
||||
fAny
|
||||
|
||||
fOmitEmpty
|
||||
|
||||
fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny
|
||||
|
||||
xmlName = "XMLName"
|
||||
)
|
||||
|
||||
func (f fieldFlags) String() string {
|
||||
switch f {
|
||||
case fAttr:
|
||||
return "attr"
|
||||
case fCDATA:
|
||||
return "cdata"
|
||||
case fCharData:
|
||||
return "chardata"
|
||||
case fInnerXML:
|
||||
return "innerxml"
|
||||
case fComment:
|
||||
return "comment"
|
||||
case fAny:
|
||||
return "any"
|
||||
case fOmitEmpty:
|
||||
return "omitempty"
|
||||
case fAny | fAttr:
|
||||
return "any,attr"
|
||||
default:
|
||||
return strconv.Itoa(int(f))
|
||||
}
|
||||
}
|
||||
|
||||
var tinfoMap sync.Map // map[reflect.Type]*typeInfo
|
||||
|
||||
// getTypeInfo returns the typeInfo structure with details necessary
|
||||
// for marshaling and unmarshaling typ.
|
||||
func getTypeInfo(typ fakereflect.TypeAndCanAddr) (*typeInfo, error) {
|
||||
if ti, ok := tinfoMap.Load(typ); ok {
|
||||
return ti.(*typeInfo), nil
|
||||
}
|
||||
|
||||
tinfo := &typeInfo{}
|
||||
named, ok := typ.Type.(*types.Named)
|
||||
if typ.IsStruct() && !(ok && named.Obj().Pkg().Path() == "encoding/xml" && named.Obj().Name() == "Name") {
|
||||
n := typ.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
f := typ.Field(i)
|
||||
if (!f.IsExported() && !f.Anonymous) || f.Tag.Get("xml") == "-" {
|
||||
continue // Private field
|
||||
}
|
||||
|
||||
// For embedded structs, embed its fields.
|
||||
if f.Anonymous {
|
||||
t := f.Type
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.IsStruct() {
|
||||
inner, err := getTypeInfo(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tinfo.xmlname == nil {
|
||||
tinfo.xmlname = inner.xmlname
|
||||
}
|
||||
for _, finfo := range inner.fields {
|
||||
finfo.idx = append([]int{i}, finfo.idx...)
|
||||
if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
finfo, err := StructFieldInfo(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Name == xmlName {
|
||||
tinfo.xmlname = finfo
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the field if it doesn't conflict with other fields.
|
||||
if err := addFieldInfo(typ, tinfo, finfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ti, _ := tinfoMap.LoadOrStore(typ, tinfo)
|
||||
return ti.(*typeInfo), nil
|
||||
}
|
||||
|
||||
// StructFieldInfo builds and returns a fieldInfo for f.
|
||||
func StructFieldInfo(f fakereflect.StructField) (*fieldInfo, error) {
|
||||
finfo := &fieldInfo{idx: f.Index}
|
||||
|
||||
// Split the tag from the xml namespace if necessary.
|
||||
tag := f.Tag.Get("xml")
|
||||
if i := strings.Index(tag, " "); i >= 0 {
|
||||
finfo.xmlns, tag = tag[:i], tag[i+1:]
|
||||
}
|
||||
|
||||
// Parse flags.
|
||||
tokens := strings.Split(tag, ",")
|
||||
if len(tokens) == 1 {
|
||||
finfo.flags = fElement
|
||||
} else {
|
||||
tag = tokens[0]
|
||||
for _, flag := range tokens[1:] {
|
||||
switch flag {
|
||||
case "attr":
|
||||
finfo.flags |= fAttr
|
||||
case "cdata":
|
||||
finfo.flags |= fCDATA
|
||||
case "chardata":
|
||||
finfo.flags |= fCharData
|
||||
case "innerxml":
|
||||
finfo.flags |= fInnerXML
|
||||
case "comment":
|
||||
finfo.flags |= fComment
|
||||
case "any":
|
||||
finfo.flags |= fAny
|
||||
case "omitempty":
|
||||
finfo.flags |= fOmitEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the flags used.
|
||||
switch mode := finfo.flags & fMode; mode {
|
||||
case 0:
|
||||
finfo.flags |= fElement
|
||||
case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
|
||||
if f.Name == xmlName {
|
||||
return nil, fmt.Errorf("cannot use option %s on XMLName field", mode)
|
||||
} else if tag != "" && mode != fAttr {
|
||||
return nil, fmt.Errorf("cannot specify name together with option ,%s", mode)
|
||||
}
|
||||
default:
|
||||
// This will also catch multiple modes in a single field.
|
||||
return nil, fmt.Errorf("invalid combination of options: %q", f.Tag.Get("xml"))
|
||||
}
|
||||
if finfo.flags&fMode == fAny {
|
||||
finfo.flags |= fElement
|
||||
}
|
||||
if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
|
||||
return nil, fmt.Errorf("can only use omitempty on elements and attributes")
|
||||
}
|
||||
}
|
||||
|
||||
// Use of xmlns without a name is not allowed.
|
||||
if finfo.xmlns != "" && tag == "" {
|
||||
return nil, fmt.Errorf("namespace without name: %q", f.Tag.Get("xml"))
|
||||
}
|
||||
|
||||
if f.Name == xmlName {
|
||||
// The XMLName field records the XML element name. Don't
|
||||
// process it as usual because its name should default to
|
||||
// empty rather than to the field name.
|
||||
finfo.name = tag
|
||||
return finfo, nil
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
// If the name part of the tag is completely empty, get
|
||||
// default from XMLName of underlying struct if feasible,
|
||||
// or field name otherwise.
|
||||
if xmlname := lookupXMLName(f.Type); xmlname != nil {
|
||||
finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
|
||||
} else {
|
||||
finfo.name = f.Name
|
||||
}
|
||||
return finfo, nil
|
||||
}
|
||||
|
||||
// Prepare field name and parents.
|
||||
parents := strings.Split(tag, ">")
|
||||
if parents[0] == "" {
|
||||
parents[0] = f.Name
|
||||
}
|
||||
if parents[len(parents)-1] == "" {
|
||||
return nil, fmt.Errorf("trailing '>'")
|
||||
}
|
||||
finfo.name = parents[len(parents)-1]
|
||||
if len(parents) > 1 {
|
||||
if (finfo.flags & fElement) == 0 {
|
||||
return nil, fmt.Errorf("%s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
|
||||
}
|
||||
finfo.parents = parents[:len(parents)-1]
|
||||
}
|
||||
|
||||
// If the field type has an XMLName field, the names must match
|
||||
// so that the behavior of both marshaling and unmarshaling
|
||||
// is straightforward and unambiguous.
|
||||
if finfo.flags&fElement != 0 {
|
||||
ftyp := f.Type
|
||||
xmlname := lookupXMLName(ftyp)
|
||||
if xmlname != nil && xmlname.name != finfo.name {
|
||||
return nil, fmt.Errorf("name %q conflicts with name %q in %s.XMLName", finfo.name, xmlname.name, ftyp)
|
||||
}
|
||||
}
|
||||
return finfo, nil
|
||||
}
|
||||
|
||||
// lookupXMLName returns the fieldInfo for typ's XMLName field
|
||||
// in case it exists and has a valid xml field tag, otherwise
|
||||
// it returns nil.
|
||||
func lookupXMLName(typ fakereflect.TypeAndCanAddr) (xmlname *fieldInfo) {
|
||||
seen := map[fakereflect.TypeAndCanAddr]struct{}{}
|
||||
for typ.IsPtr() {
|
||||
typ = typ.Elem()
|
||||
if _, ok := seen[typ]; ok {
|
||||
// Loop in type graph, e.g. 'type P *P'
|
||||
return nil
|
||||
}
|
||||
seen[typ] = struct{}{}
|
||||
}
|
||||
if !typ.IsStruct() {
|
||||
return nil
|
||||
}
|
||||
for i, n := 0, typ.NumField(); i < n; i++ {
|
||||
f := typ.Field(i)
|
||||
if f.Name != xmlName {
|
||||
continue
|
||||
}
|
||||
finfo, err := StructFieldInfo(f)
|
||||
if err == nil && finfo.name != "" {
|
||||
return finfo
|
||||
}
|
||||
// Also consider errors as a non-existent field tag
|
||||
// and let getTypeInfo itself report the error.
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// addFieldInfo adds finfo to tinfo.fields if there are no
|
||||
// conflicts, or if conflicts arise from previous fields that were
|
||||
// obtained from deeper embedded structures than finfo. In the latter
|
||||
// case, the conflicting entries are dropped.
|
||||
// A conflict occurs when the path (parent + name) to a field is
|
||||
// itself a prefix of another path, or when two paths match exactly.
|
||||
// It is okay for field paths to share a common, shorter prefix.
|
||||
func addFieldInfo(typ fakereflect.TypeAndCanAddr, tinfo *typeInfo, newf *fieldInfo) error {
|
||||
var conflicts []int
|
||||
Loop:
|
||||
// First, figure all conflicts. Most working code will have none.
|
||||
for i := range tinfo.fields {
|
||||
oldf := &tinfo.fields[i]
|
||||
if oldf.flags&fMode != newf.flags&fMode {
|
||||
continue
|
||||
}
|
||||
if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
|
||||
continue
|
||||
}
|
||||
minl := min(len(newf.parents), len(oldf.parents))
|
||||
for p := 0; p < minl; p++ {
|
||||
if oldf.parents[p] != newf.parents[p] {
|
||||
continue Loop
|
||||
}
|
||||
}
|
||||
if len(oldf.parents) > len(newf.parents) {
|
||||
if oldf.parents[len(newf.parents)] == newf.name {
|
||||
conflicts = append(conflicts, i)
|
||||
}
|
||||
} else if len(oldf.parents) < len(newf.parents) {
|
||||
if newf.parents[len(oldf.parents)] == oldf.name {
|
||||
conflicts = append(conflicts, i)
|
||||
}
|
||||
} else {
|
||||
if newf.name == oldf.name {
|
||||
conflicts = append(conflicts, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Without conflicts, add the new field and return.
|
||||
if conflicts == nil {
|
||||
tinfo.fields = append(tinfo.fields, *newf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// If any conflict is shallower, ignore the new field.
|
||||
// This matches the Go field resolution on embedding.
|
||||
for _, i := range conflicts {
|
||||
if len(tinfo.fields[i].idx) < len(newf.idx) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, if any of them is at the same depth level, it's an error.
|
||||
for _, i := range conflicts {
|
||||
oldf := &tinfo.fields[i]
|
||||
if len(oldf.idx) == len(newf.idx) {
|
||||
f1 := typ.FieldByIndex(oldf.idx)
|
||||
f2 := typ.FieldByIndex(newf.idx)
|
||||
return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the new field is shallower, and thus takes precedence,
|
||||
// so drop the conflicting fields from tinfo and append the new one.
|
||||
for c := len(conflicts) - 1; c >= 0; c-- {
|
||||
i := conflicts[c]
|
||||
copy(tinfo.fields[i:], tinfo.fields[i+1:])
|
||||
tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
|
||||
}
|
||||
tinfo.fields = append(tinfo.fields, *newf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A TagPathError represents an error in the unmarshaling process
|
||||
// caused by the use of field tags with conflicting paths.
|
||||
type TagPathError struct {
|
||||
Struct fakereflect.TypeAndCanAddr
|
||||
Field1, Tag1 string
|
||||
Field2, Tag2 string
|
||||
}
|
||||
|
||||
func (e *TagPathError) Error() string {
|
||||
return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
|
||||
}
|
||||
|
||||
// value returns v's field value corresponding to finfo.
|
||||
// It's equivalent to v.FieldByIndex(finfo.idx), but when passed
|
||||
// initNilPointers, it initializes and dereferences pointers as necessary.
|
||||
// When passed dontInitNilPointers and a nil pointer is reached, the function
|
||||
// returns a zero reflect.Value.
|
||||
func (finfo *fieldInfo) value(v fakereflect.TypeAndCanAddr) fakereflect.TypeAndCanAddr {
|
||||
for i, x := range finfo.idx {
|
||||
if i > 0 {
|
||||
t := v
|
||||
if t.IsPtr() && t.Elem().IsStruct() {
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
v = v.Field(x).Type
|
||||
}
|
||||
return v
|
||||
}
|
33
vendor/honnef.co/go/tools/staticcheck/fakexml/xml.go
vendored
Normal file
33
vendor/honnef.co/go/tools/staticcheck/fakexml/xml.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakexml
|
||||
|
||||
// References:
|
||||
// Annotated XML spec: https://www.xml.com/axml/testaxml.htm
|
||||
// XML name spaces: https://www.w3.org/TR/REC-xml-names/
|
||||
|
||||
// TODO(rsc):
|
||||
// Test error handling.
|
||||
|
||||
// A Name represents an XML name (Local) annotated
|
||||
// with a name space identifier (Space).
|
||||
// In tokens returned by Decoder.Token, the Space identifier
|
||||
// is given as a canonical URL, not the short prefix used
|
||||
// in the document being parsed.
|
||||
type Name struct {
|
||||
Space, Local string
|
||||
}
|
||||
|
||||
// An Attr represents an attribute in an XML element (Name=Value).
|
||||
type Attr struct {
|
||||
Name Name
|
||||
Value string
|
||||
}
|
||||
|
||||
// A StartElement represents an XML start element.
|
||||
type StartElement struct {
|
||||
Name Name
|
||||
Attr []Attr
|
||||
}
|
Reference in New Issue
Block a user