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
327
vendor/honnef.co/go/tools/staticcheck/analysis.go
vendored
Normal file
327
vendor/honnef.co/go/tools/staticcheck/analysis.go
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"honnef.co/go/tools/analysis/facts"
|
||||
"honnef.co/go/tools/analysis/facts/nilness"
|
||||
"honnef.co/go/tools/analysis/facts/typedness"
|
||||
"honnef.co/go/tools/analysis/lint"
|
||||
"honnef.co/go/tools/internal/passes/buildir"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
)
|
||||
|
||||
func makeCallCheckerAnalyzer(rules map[string]CallCheck, extraReqs ...*analysis.Analyzer) *analysis.Analyzer {
|
||||
reqs := []*analysis.Analyzer{buildir.Analyzer, facts.TokenFile}
|
||||
reqs = append(reqs, extraReqs...)
|
||||
return &analysis.Analyzer{
|
||||
Run: callChecker(rules),
|
||||
Requires: reqs,
|
||||
}
|
||||
}
|
||||
|
||||
var Analyzers = lint.InitializeAnalyzers(Docs, map[string]*analysis.Analyzer{
|
||||
"SA1000": makeCallCheckerAnalyzer(checkRegexpRules),
|
||||
"SA1001": {
|
||||
Run: CheckTemplate,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1002": makeCallCheckerAnalyzer(checkTimeParseRules),
|
||||
"SA1003": makeCallCheckerAnalyzer(checkEncodingBinaryRules),
|
||||
"SA1004": {
|
||||
Run: CheckTimeSleepConstant,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1005": {
|
||||
Run: CheckExec,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1006": {
|
||||
Run: CheckUnsafePrintf,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1007": makeCallCheckerAnalyzer(checkURLsRules),
|
||||
"SA1008": {
|
||||
Run: CheckCanonicalHeaderKey,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1010": makeCallCheckerAnalyzer(checkRegexpFindAllRules),
|
||||
"SA1011": makeCallCheckerAnalyzer(checkUTF8CutsetRules),
|
||||
"SA1012": {
|
||||
Run: CheckNilContext,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1013": {
|
||||
Run: CheckSeeker,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1014": makeCallCheckerAnalyzer(checkUnmarshalPointerRules),
|
||||
"SA1015": {
|
||||
Run: CheckLeakyTimeTick,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1016": {
|
||||
Run: CheckUntrappableSignal,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA1017": makeCallCheckerAnalyzer(checkUnbufferedSignalChanRules),
|
||||
"SA1018": makeCallCheckerAnalyzer(checkStringsReplaceZeroRules),
|
||||
"SA1019": {
|
||||
Run: CheckDeprecated,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Deprecated, facts.Generated},
|
||||
},
|
||||
"SA1020": makeCallCheckerAnalyzer(checkListenAddressRules),
|
||||
"SA1021": makeCallCheckerAnalyzer(checkBytesEqualIPRules),
|
||||
"SA1023": {
|
||||
Run: CheckWriterBufferModified,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1024": makeCallCheckerAnalyzer(checkUniqueCutsetRules),
|
||||
"SA1025": {
|
||||
Run: CheckTimerResetReturnValue,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1026": makeCallCheckerAnalyzer(checkUnsupportedMarshal),
|
||||
"SA1027": makeCallCheckerAnalyzer(checkAtomicAlignment),
|
||||
"SA1028": makeCallCheckerAnalyzer(checkSortSliceRules),
|
||||
"SA1029": makeCallCheckerAnalyzer(checkWithValueKeyRules),
|
||||
"SA1030": makeCallCheckerAnalyzer(checkStrconvRules),
|
||||
|
||||
"SA2000": {
|
||||
Run: CheckWaitgroupAdd,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA2001": {
|
||||
Run: CheckEmptyCriticalSection,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA2002": {
|
||||
Run: CheckConcurrentTesting,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA2003": {
|
||||
Run: CheckDeferLock,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
|
||||
"SA3000": {
|
||||
Run: CheckTestMainExit,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA3001": {
|
||||
Run: CheckBenchmarkN,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
|
||||
"SA4000": {
|
||||
Run: CheckLhsRhsIdentical,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.TokenFile, facts.Generated},
|
||||
},
|
||||
"SA4001": {
|
||||
Run: CheckIneffectiveCopy,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4003": {
|
||||
Run: CheckExtremeComparison,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4004": {
|
||||
Run: CheckIneffectiveLoop,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4005": {
|
||||
Run: CheckIneffectiveFieldAssignments,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4006": {
|
||||
Run: CheckUnreadVariableValues,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.Generated},
|
||||
},
|
||||
"SA4008": {
|
||||
Run: CheckLoopCondition,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4009": {
|
||||
Run: CheckArgOverwritten,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4010": {
|
||||
Run: CheckIneffectiveAppend,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4011": {
|
||||
Run: CheckScopedBreak,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4012": {
|
||||
Run: CheckNaNComparison,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4013": {
|
||||
Run: CheckDoubleNegation,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4014": {
|
||||
Run: CheckRepeatedIfElse,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4015": makeCallCheckerAnalyzer(checkMathIntRules),
|
||||
"SA4016": {
|
||||
Run: CheckSillyBitwiseOps,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.TokenFile},
|
||||
},
|
||||
"SA4017": {
|
||||
Run: CheckPureFunctions,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.Purity},
|
||||
},
|
||||
"SA4018": {
|
||||
Run: CheckSelfAssignment,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile, facts.Purity},
|
||||
},
|
||||
"SA4019": {
|
||||
Run: CheckDuplicateBuildConstraints,
|
||||
Requires: []*analysis.Analyzer{facts.Generated},
|
||||
},
|
||||
"SA4020": {
|
||||
Run: CheckUnreachableTypeCases,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4021": {
|
||||
Run: CheckSingleArgAppend,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile},
|
||||
},
|
||||
"SA4022": {
|
||||
Run: CheckAddressIsNil,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4023": {
|
||||
Run: CheckTypedNilInterface,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, typedness.Analysis, nilness.Analysis},
|
||||
},
|
||||
"SA4024": {
|
||||
Run: CheckBuiltinZeroComparison,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4025": {
|
||||
Run: CheckIntegerDivisionEqualsZero,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4026": {
|
||||
Run: CheckNegativeZeroFloat,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4027": {
|
||||
Run: CheckIneffectiveURLQueryModification,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4028": {
|
||||
Run: CheckModuloOne,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4029": {
|
||||
Run: CheckIneffectiveSort,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4030": {
|
||||
Run: CheckIneffectiveRandInt,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA4031": {
|
||||
Run: CheckAllocationNilCheck,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer, facts.TokenFile},
|
||||
},
|
||||
|
||||
"SA5000": {
|
||||
Run: CheckNilMaps,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5001": {
|
||||
Run: CheckEarlyDefer,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA5002": {
|
||||
Run: CheckInfiniteEmptyLoop,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA5003": {
|
||||
Run: CheckDeferInInfiniteLoop,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA5004": {
|
||||
Run: CheckLoopEmptyDefault,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA5005": {
|
||||
Run: CheckCyclicFinalizer,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5007": {
|
||||
Run: CheckInfiniteRecursion,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5008": {
|
||||
Run: CheckStructTags,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA5009": makeCallCheckerAnalyzer(checkPrintfRules),
|
||||
"SA5010": {
|
||||
Run: CheckImpossibleTypeAssertion,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.TokenFile},
|
||||
},
|
||||
"SA5011": {
|
||||
Run: CheckMaybeNil,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5012": {
|
||||
Run: CheckEvenSliceLength,
|
||||
FactTypes: []analysis.Fact{new(evenElements)},
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
|
||||
"SA6000": makeCallCheckerAnalyzer(checkRegexpMatchLoopRules),
|
||||
"SA6001": {
|
||||
Run: CheckMapBytesKey,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA6002": makeCallCheckerAnalyzer(checkSyncPoolValueRules),
|
||||
"SA6003": {
|
||||
Run: CheckRangeStringRunes,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA6005": {
|
||||
Run: CheckToLowerToUpperComparison,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
|
||||
"SA9001": {
|
||||
Run: CheckDubiousDeferInChannelRangeLoop,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA9002": {
|
||||
Run: CheckNonOctalFileMode,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA9003": {
|
||||
Run: CheckEmptyBranch,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.TokenFile, facts.Generated},
|
||||
},
|
||||
"SA9004": {
|
||||
Run: CheckMissingEnumTypesInDeclaration,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
// Filtering generated code because it may include empty structs generated from data models.
|
||||
"SA9005": makeCallCheckerAnalyzer(checkNoopMarshal, facts.Generated),
|
||||
"SA9006": {
|
||||
Run: CheckStaticBitShift,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
"SA9007": {
|
||||
Run: CheckBadRemoveAll,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
|
||||
"SA9008": {
|
||||
Run: CheckTypeAssertionShadowingElse,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, buildir.Analyzer, facts.TokenFile},
|
||||
},
|
||||
})
|
21
vendor/honnef.co/go/tools/staticcheck/buildtag.go
vendored
Normal file
21
vendor/honnef.co/go/tools/staticcheck/buildtag.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"honnef.co/go/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
func buildTags(f *ast.File) [][]string {
|
||||
var out [][]string
|
||||
for _, line := range strings.Split(astutil.Preamble(f), "\n") {
|
||||
if !strings.HasPrefix(line, "+build ") {
|
||||
continue
|
||||
}
|
||||
line = strings.TrimSpace(strings.TrimPrefix(line, "+build "))
|
||||
fields := strings.Fields(line)
|
||||
out = append(out, fields)
|
||||
}
|
||||
return out
|
||||
}
|
1302
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
Normal file
1302
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
382
vendor/honnef.co/go/tools/staticcheck/fakejson/encode.go
vendored
Normal file
382
vendor/honnef.co/go/tools/staticcheck/fakejson/encode.go
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
// Copyright 2010 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/json 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/json.
|
||||
// This is used for SA1026.
|
||||
|
||||
package fakejson
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/exp/typeparams"
|
||||
"honnef.co/go/tools/staticcheck/fakereflect"
|
||||
)
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) string {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func Marshal(v types.Type) *UnsupportedTypeError {
|
||||
enc := encoder{
|
||||
seen: map[fakereflect.TypeAndCanAddr]struct{}{},
|
||||
}
|
||||
return enc.newTypeEncoder(fakereflect.TypeAndCanAddr{Type: v}, "x")
|
||||
}
|
||||
|
||||
// An UnsupportedTypeError is returned by Marshal when attempting
|
||||
// to encode an unsupported value type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type types.Type
|
||||
Path string
|
||||
}
|
||||
|
||||
var marshalerType = types.NewInterfaceType([]*types.Func{
|
||||
types.NewFunc(token.NoPos, nil, "MarshalJSON", 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 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()
|
||||
|
||||
type encoder struct {
|
||||
seen map[fakereflect.TypeAndCanAddr]struct{}
|
||||
}
|
||||
|
||||
func (enc *encoder) newTypeEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
|
||||
if _, ok := enc.seen[t]; ok {
|
||||
return nil
|
||||
}
|
||||
enc.seen[t] = struct{}{}
|
||||
|
||||
if t.Implements(marshalerType) {
|
||||
return nil
|
||||
}
|
||||
if !t.IsPtr() && t.CanAddr() && fakereflect.PtrTo(t).Implements(marshalerType) {
|
||||
return nil
|
||||
}
|
||||
if t.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
if !t.IsPtr() && t.CanAddr() && fakereflect.PtrTo(t).Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch t.Type.Underlying().(type) {
|
||||
case *types.Basic, *types.Interface:
|
||||
return nil
|
||||
case *types.Struct:
|
||||
return enc.typeFields(t, stack)
|
||||
case *types.Map:
|
||||
return enc.newMapEncoder(t, stack)
|
||||
case *types.Slice:
|
||||
return enc.newSliceEncoder(t, stack)
|
||||
case *types.Array:
|
||||
return enc.newArrayEncoder(t, stack)
|
||||
case *types.Pointer:
|
||||
// we don't have to express the pointer dereference in the path; x.f is syntactic sugar for (*x).f
|
||||
return enc.newTypeEncoder(t.Elem(), stack)
|
||||
default:
|
||||
return &UnsupportedTypeError{t.Type, stack}
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *encoder) newMapEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
|
||||
if typeparams.IsTypeParam(t.Key().Type) {
|
||||
// We don't know enough about the concrete instantiation to say much about the key. The only time we could make
|
||||
// a definite "this key is bad" statement is if the type parameter is constrained by type terms, none of which
|
||||
// are tilde terms, none of which are a basic type. In all other cases, the key might implement TextMarshaler.
|
||||
// It doesn't seem worth checking for that one single case.
|
||||
return enc.newTypeEncoder(t.Elem(), stack+"[k]")
|
||||
}
|
||||
|
||||
switch t.Key().Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
default:
|
||||
if !t.Key().Implements(textMarshalerType) {
|
||||
return &UnsupportedTypeError{
|
||||
Type: t.Type,
|
||||
Path: stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
return enc.newTypeEncoder(t.Elem(), stack+"[k]")
|
||||
}
|
||||
|
||||
func (enc *encoder) newSliceEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
|
||||
// Byte slices get special treatment; arrays don't.
|
||||
basic, ok := t.Elem().Type.Underlying().(*types.Basic)
|
||||
if ok && basic.Kind() == types.Uint8 {
|
||||
p := fakereflect.PtrTo(t.Elem())
|
||||
if !p.Implements(marshalerType) && !p.Implements(textMarshalerType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return enc.newArrayEncoder(t, stack)
|
||||
}
|
||||
|
||||
func (enc *encoder) newArrayEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
|
||||
return enc.newTypeEncoder(t.Elem(), stack+"[0]")
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func typeByIndex(t fakereflect.TypeAndCanAddr, index []int) fakereflect.TypeAndCanAddr {
|
||||
for _, i := range index {
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
t = t.Field(i).Type
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// A field represents a single field found in a struct.
|
||||
type field struct {
|
||||
name string
|
||||
|
||||
tag bool
|
||||
index []int
|
||||
typ fakereflect.TypeAndCanAddr
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
for k, xik := range x[i].index {
|
||||
if k >= len(x[j].index) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].index[k] {
|
||||
return xik < x[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
|
||||
// typeFields returns a list of fields that JSON should recognize for the given type.
|
||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
||||
// and then any reachable anonymous structs.
|
||||
func (enc *encoder) typeFields(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []field{}
|
||||
next := []field{{typ: t}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
var count, nextCount map[fakereflect.TypeAndCanAddr]int
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[fakereflect.TypeAndCanAddr]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []field
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[fakereflect.TypeAndCanAddr]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.typ] {
|
||||
continue
|
||||
}
|
||||
visited[f.typ] = true
|
||||
|
||||
// Scan f.typ for fields to include.
|
||||
for i := 0; i < f.typ.NumField(); i++ {
|
||||
sf := f.typ.Field(i)
|
||||
if sf.Anonymous {
|
||||
t := sf.Type
|
||||
if t.IsPtr() {
|
||||
t = t.Elem()
|
||||
}
|
||||
if !sf.IsExported() && !t.IsStruct() {
|
||||
// Ignore embedded fields of unexported non-struct types.
|
||||
continue
|
||||
}
|
||||
// Do not ignore embedded fields of unexported struct types
|
||||
// since they may have exported fields.
|
||||
} else if !sf.IsExported() {
|
||||
// Ignore unexported non-embedded fields.
|
||||
continue
|
||||
}
|
||||
tag := sf.Tag.Get("json")
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
name := parseTag(tag)
|
||||
if !isValidTag(name) {
|
||||
name = ""
|
||||
}
|
||||
index := make([]int, len(f.index)+1)
|
||||
copy(index, f.index)
|
||||
index[len(f.index)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.IsPtr() {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if name != "" || !sf.Anonymous || !ft.IsStruct() {
|
||||
tagged := name != ""
|
||||
if name == "" {
|
||||
name = sf.Name
|
||||
}
|
||||
field := field{
|
||||
name: name,
|
||||
tag: tagged,
|
||||
index: index,
|
||||
typ: ft,
|
||||
}
|
||||
|
||||
fields = append(fields, field)
|
||||
if count[f.typ] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
next = append(next, field{name: ft.Name(), index: index, typ: ft})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(fields, func(i, j int) bool {
|
||||
x := fields
|
||||
// sort field by name, breaking ties with depth, then
|
||||
// breaking ties with "name came from json tag", then
|
||||
// breaking ties with index sequence.
|
||||
if x[i].name != x[j].name {
|
||||
return x[i].name < x[j].name
|
||||
}
|
||||
if len(x[i].index) != len(x[j].index) {
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
if x[i].tag != x[j].tag {
|
||||
return x[i].tag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
})
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with JSON tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
sort.Sort(byIndex(fields))
|
||||
|
||||
for i := range fields {
|
||||
f := &fields[i]
|
||||
err := enc.newTypeEncoder(typeByIndex(t, f.index), stack+pathByIndex(t, f.index))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// JSON tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []field) (field, bool) {
|
||||
// The fields are sorted in increasing index-length order, then by presence of tag.
|
||||
// That means that the first field is the dominant one. We need only check
|
||||
// for error cases: two fields at top level, either both tagged or neither tagged.
|
||||
if len(fields) > 1 && len(fields[0].index) == len(fields[1].index) && fields[0].tag == fields[1].tag {
|
||||
return field{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
131
vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go
vendored
Normal file
131
vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
package fakereflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type TypeAndCanAddr struct {
|
||||
Type types.Type
|
||||
canAddr bool
|
||||
}
|
||||
|
||||
type StructField struct {
|
||||
Index []int
|
||||
Name string
|
||||
Anonymous bool
|
||||
Tag reflect.StructTag
|
||||
f *types.Var
|
||||
Type TypeAndCanAddr
|
||||
}
|
||||
|
||||
func (sf StructField) IsExported() bool { return sf.f.Exported() }
|
||||
|
||||
func (t TypeAndCanAddr) Field(i int) StructField {
|
||||
st := t.Type.Underlying().(*types.Struct)
|
||||
f := st.Field(i)
|
||||
return StructField{
|
||||
f: f,
|
||||
Index: []int{i},
|
||||
Name: f.Name(),
|
||||
Anonymous: f.Anonymous(),
|
||||
Tag: reflect.StructTag(st.Tag(i)),
|
||||
Type: TypeAndCanAddr{
|
||||
Type: f.Type(),
|
||||
canAddr: t.canAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) FieldByIndex(index []int) StructField {
|
||||
f := t.Field(index[0])
|
||||
for _, idx := range index[1:] {
|
||||
f = f.Type.Field(idx)
|
||||
}
|
||||
f.Index = index
|
||||
return f
|
||||
}
|
||||
|
||||
func PtrTo(t TypeAndCanAddr) TypeAndCanAddr {
|
||||
// Note that we don't care about canAddr here because it's irrelevant to all uses of PtrTo
|
||||
return TypeAndCanAddr{Type: types.NewPointer(t.Type)}
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) CanAddr() bool { return t.canAddr }
|
||||
|
||||
func (t TypeAndCanAddr) Implements(ityp *types.Interface) bool {
|
||||
return types.Implements(t.Type, ityp)
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) IsSlice() bool {
|
||||
_, ok := t.Type.Underlying().(*types.Slice)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) IsArray() bool {
|
||||
_, ok := t.Type.Underlying().(*types.Array)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) IsPtr() bool {
|
||||
_, ok := t.Type.Underlying().(*types.Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) IsInterface() bool {
|
||||
_, ok := t.Type.Underlying().(*types.Interface)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) IsStruct() bool {
|
||||
_, ok := t.Type.Underlying().(*types.Struct)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) Name() string {
|
||||
named, ok := t.Type.(*types.Named)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return named.Obj().Name()
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) NumField() int {
|
||||
return t.Type.Underlying().(*types.Struct).NumFields()
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) String() string {
|
||||
return t.Type.String()
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) Key() TypeAndCanAddr {
|
||||
return TypeAndCanAddr{Type: t.Type.Underlying().(*types.Map).Key()}
|
||||
}
|
||||
|
||||
func (t TypeAndCanAddr) Elem() TypeAndCanAddr {
|
||||
switch typ := t.Type.Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
return TypeAndCanAddr{
|
||||
Type: typ.Elem(),
|
||||
canAddr: true,
|
||||
}
|
||||
case *types.Slice:
|
||||
return TypeAndCanAddr{
|
||||
Type: typ.Elem(),
|
||||
canAddr: true,
|
||||
}
|
||||
case *types.Array:
|
||||
return TypeAndCanAddr{
|
||||
Type: typ.Elem(),
|
||||
canAddr: t.canAddr,
|
||||
}
|
||||
case *types.Map:
|
||||
return TypeAndCanAddr{
|
||||
Type: typ.Elem(),
|
||||
canAddr: false,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled type %T", typ))
|
||||
}
|
||||
}
|
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
|
||||
}
|
5215
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
Normal file
5215
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
294
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
Normal file
294
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"honnef.co/go/tools/analysis/code"
|
||||
"honnef.co/go/tools/go/ir"
|
||||
"honnef.co/go/tools/go/ir/irutil"
|
||||
"honnef.co/go/tools/go/types/typeutil"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const (
|
||||
MsgInvalidHostPort = "invalid port or service name in host:port pair"
|
||||
MsgInvalidUTF8 = "argument is not a valid UTF-8 encoded string"
|
||||
MsgNonUniqueCutset = "cutset contains duplicate characters"
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Pass *analysis.Pass
|
||||
Instr ir.CallInstruction
|
||||
Args []*Argument
|
||||
|
||||
Parent *ir.Function
|
||||
|
||||
invalids []string
|
||||
}
|
||||
|
||||
func (c *Call) Invalid(msg string) {
|
||||
c.invalids = append(c.invalids, msg)
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Value Value
|
||||
invalids []string
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Value ir.Value
|
||||
}
|
||||
|
||||
func (arg *Argument) Invalid(msg string) {
|
||||
arg.invalids = append(arg.invalids, msg)
|
||||
}
|
||||
|
||||
type CallCheck func(call *Call)
|
||||
|
||||
func extractConstExpectKind(v ir.Value, kind constant.Kind) *ir.Const {
|
||||
k := extractConst(v)
|
||||
if k == nil || k.Value == nil || k.Value.Kind() != kind {
|
||||
return nil
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func extractConst(v ir.Value) *ir.Const {
|
||||
v = irutil.Flatten(v)
|
||||
switch v := v.(type) {
|
||||
case *ir.Const:
|
||||
return v
|
||||
case *ir.MakeInterface:
|
||||
return extractConst(v.X)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateRegexp(v Value) error {
|
||||
if c := extractConstExpectKind(v.Value, constant.String); c != nil {
|
||||
s := constant.StringVal(c.Value)
|
||||
if _, err := regexp.Compile(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateTimeLayout(v Value) error {
|
||||
if c := extractConstExpectKind(v.Value, constant.String); c != nil {
|
||||
s := constant.StringVal(c.Value)
|
||||
s = strings.Replace(s, "_", " ", -1)
|
||||
s = strings.Replace(s, "Z", "-", -1)
|
||||
_, err := time.Parse(s, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateURL(v Value) error {
|
||||
if c := extractConstExpectKind(v.Value, constant.String); c != nil {
|
||||
s := constant.StringVal(c.Value)
|
||||
_, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q is not a valid URL: %s", s, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InvalidUTF8(v Value) bool {
|
||||
if c := extractConstExpectKind(v.Value, constant.String); c != nil {
|
||||
s := constant.StringVal(c.Value)
|
||||
if !utf8.ValidString(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func UnbufferedChannel(v Value) bool {
|
||||
// TODO(dh): this check of course misses many cases of unbuffered
|
||||
// channels, such as any in phi or sigma nodes. We'll eventually
|
||||
// replace this function.
|
||||
val := v.Value
|
||||
if ct, ok := val.(*ir.ChangeType); ok {
|
||||
val = ct.X
|
||||
}
|
||||
mk, ok := val.(*ir.MakeChan)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if k, ok := mk.Size.(*ir.Const); ok && k.Value.Kind() == constant.Int {
|
||||
if v, ok := constant.Int64Val(k.Value); ok && v == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Pointer(v Value) bool {
|
||||
switch v.Value.Type().Underlying().(type) {
|
||||
case *types.Pointer, *types.Interface:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ConvertedFromInt(v Value) bool {
|
||||
conv, ok := v.Value.(*ir.Convert)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
b, ok := conv.X.Type().Underlying().(*types.Basic)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if (b.Info() & types.IsInteger) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool {
|
||||
typ = typ.Underlying()
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
case types.Uint8, types.Uint16, types.Uint32, types.Uint64,
|
||||
types.Int8, types.Int16, types.Int32, types.Int64,
|
||||
types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
|
||||
return true
|
||||
case types.Bool:
|
||||
return code.IsGoVersion(pass, 8)
|
||||
}
|
||||
return false
|
||||
case *types.Struct:
|
||||
n := typ.NumFields()
|
||||
for i := 0; i < n; i++ {
|
||||
if !validEncodingBinaryType(pass, typ.Field(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Array:
|
||||
return validEncodingBinaryType(pass, typ.Elem())
|
||||
case *types.Interface:
|
||||
// we can't determine if it's a valid type or not
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CanBinaryMarshal(pass *analysis.Pass, v Value) bool {
|
||||
typ := v.Value.Type().Underlying()
|
||||
if ttyp, ok := typ.(*types.Pointer); ok {
|
||||
typ = ttyp.Elem().Underlying()
|
||||
}
|
||||
if ttyp, ok := typ.(interface {
|
||||
Elem() types.Type
|
||||
}); ok {
|
||||
if _, ok := ttyp.(*types.Pointer); !ok {
|
||||
typ = ttyp.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
return validEncodingBinaryType(pass, typ)
|
||||
}
|
||||
|
||||
func RepeatZeroTimes(name string, arg int) CallCheck {
|
||||
return func(call *Call) {
|
||||
arg := call.Args[arg]
|
||||
if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int {
|
||||
if v, ok := constant.Int64Val(k.Value); ok && v == 0 {
|
||||
arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateServiceName(s string) bool {
|
||||
if len(s) < 1 || len(s) > 15 {
|
||||
return false
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(s, "--") {
|
||||
return false
|
||||
}
|
||||
hasLetter := false
|
||||
for _, r := range s {
|
||||
if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
|
||||
hasLetter = true
|
||||
continue
|
||||
}
|
||||
if r >= '0' && r <= '9' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return hasLetter
|
||||
}
|
||||
|
||||
func validatePort(s string) bool {
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return validateServiceName(s)
|
||||
}
|
||||
return n >= 0 && n <= 65535
|
||||
}
|
||||
|
||||
func ValidHostPort(v Value) bool {
|
||||
if k := extractConstExpectKind(v.Value, constant.String); k != nil {
|
||||
s := constant.StringVal(k.Value)
|
||||
if s == "" {
|
||||
return true
|
||||
}
|
||||
_, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// TODO(dh): check hostname
|
||||
if !validatePort(port) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ConvertedFrom reports whether value v was converted from type typ.
|
||||
func ConvertedFrom(v Value, typ string) bool {
|
||||
change, ok := v.Value.(*ir.ChangeType)
|
||||
return ok && typeutil.IsType(change.X.Type(), typ)
|
||||
}
|
||||
|
||||
func UniqueStringCutset(v Value) bool {
|
||||
if c := extractConstExpectKind(v.Value, constant.String); c != nil {
|
||||
s := constant.StringVal(c.Value)
|
||||
rs := runeSlice(s)
|
||||
if len(rs) < 2 {
|
||||
return true
|
||||
}
|
||||
sort.Sort(rs)
|
||||
for i, r := range rs[1:] {
|
||||
if rs[i] == r {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
58
vendor/honnef.co/go/tools/staticcheck/structtag.go
vendored
Normal file
58
vendor/honnef.co/go/tools/staticcheck/structtag.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2019 Dominik Honnef. All rights reserved.
|
||||
|
||||
package staticcheck
|
||||
|
||||
import "strconv"
|
||||
|
||||
func parseStructTag(tag string) (map[string][]string, error) {
|
||||
// FIXME(dh): detect missing closing quote
|
||||
out := map[string][]string{}
|
||||
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
name := string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
qvalue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
|
||||
value, err := strconv.Unquote(qvalue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = append(out[name], value)
|
||||
}
|
||||
return out, nil
|
||||
}
|
Reference in New Issue
Block a user