Update and fix staticcheck
This commit is contained in:
@ -1,15 +0,0 @@
|
||||
# Contributing to staticcheck
|
||||
|
||||
## Before filing an issue:
|
||||
|
||||
### Are you having trouble building staticcheck?
|
||||
|
||||
Check you have the latest version of its dependencies. Run
|
||||
```
|
||||
go get -u honnef.co/go/tools/staticcheck
|
||||
```
|
||||
If you still have problems, consider searching for existing issues before filing a new issue.
|
||||
|
||||
## Before sending a pull request:
|
||||
|
||||
Have you understood the purpose of staticcheck? Make sure to carefully read `README`.
|
382
vendor/honnef.co/go/tools/staticcheck/analysis.go
vendored
382
vendor/honnef.co/go/tools/staticcheck/analysis.go
vendored
@ -1,525 +1,267 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"honnef.co/go/tools/facts"
|
||||
"honnef.co/go/tools/internal/passes/buildssa"
|
||||
"honnef.co/go/tools/internal/passes/buildir"
|
||||
"honnef.co/go/tools/lint/lintutil"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
)
|
||||
|
||||
func newFlagSet() flag.FlagSet {
|
||||
fs := flag.NewFlagSet("", flag.PanicOnError)
|
||||
fs.Var(lintutil.NewVersionFlag(), "go", "Target Go version")
|
||||
return *fs
|
||||
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 = map[string]*analysis.Analyzer{
|
||||
"SA1000": {
|
||||
Name: "SA1000",
|
||||
Run: callChecker(checkRegexpRules),
|
||||
Doc: Docs["SA1000"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
var Analyzers = lintutil.InitializeAnalyzers(Docs, map[string]*analysis.Analyzer{
|
||||
"SA1000": makeCallCheckerAnalyzer(checkRegexpRules),
|
||||
"SA1001": {
|
||||
Name: "SA1001",
|
||||
Run: CheckTemplate,
|
||||
Doc: Docs["SA1001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1002": {
|
||||
Name: "SA1002",
|
||||
Run: callChecker(checkTimeParseRules),
|
||||
Doc: Docs["SA1002"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1003": {
|
||||
Name: "SA1003",
|
||||
Run: callChecker(checkEncodingBinaryRules),
|
||||
Doc: Docs["SA1003"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1002": makeCallCheckerAnalyzer(checkTimeParseRules),
|
||||
"SA1003": makeCallCheckerAnalyzer(checkEncodingBinaryRules),
|
||||
"SA1004": {
|
||||
Name: "SA1004",
|
||||
Run: CheckTimeSleepConstant,
|
||||
Doc: Docs["SA1004"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1005": {
|
||||
Name: "SA1005",
|
||||
Run: CheckExec,
|
||||
Doc: Docs["SA1005"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1006": {
|
||||
Name: "SA1006",
|
||||
Run: CheckUnsafePrintf,
|
||||
Doc: Docs["SA1006"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1007": {
|
||||
Name: "SA1007",
|
||||
Run: callChecker(checkURLsRules),
|
||||
Doc: Docs["SA1007"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1007": makeCallCheckerAnalyzer(checkURLsRules),
|
||||
"SA1008": {
|
||||
Name: "SA1008",
|
||||
Run: CheckCanonicalHeaderKey,
|
||||
Doc: Docs["SA1008"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1010": {
|
||||
Name: "SA1010",
|
||||
Run: callChecker(checkRegexpFindAllRules),
|
||||
Doc: Docs["SA1010"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1011": {
|
||||
Name: "SA1011",
|
||||
Run: callChecker(checkUTF8CutsetRules),
|
||||
Doc: Docs["SA1011"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1010": makeCallCheckerAnalyzer(checkRegexpFindAllRules),
|
||||
"SA1011": makeCallCheckerAnalyzer(checkUTF8CutsetRules),
|
||||
"SA1012": {
|
||||
Name: "SA1012",
|
||||
Run: CheckNilContext,
|
||||
Doc: Docs["SA1012"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1013": {
|
||||
Name: "SA1013",
|
||||
Run: CheckSeeker,
|
||||
Doc: Docs["SA1013"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1014": {
|
||||
Name: "SA1014",
|
||||
Run: callChecker(checkUnmarshalPointerRules),
|
||||
Doc: Docs["SA1014"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1014": makeCallCheckerAnalyzer(checkUnmarshalPointerRules),
|
||||
"SA1015": {
|
||||
Name: "SA1015",
|
||||
Run: CheckLeakyTimeTick,
|
||||
Doc: Docs["SA1015"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1016": {
|
||||
Name: "SA1016",
|
||||
Run: CheckUntrappableSignal,
|
||||
Doc: Docs["SA1016"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1017": {
|
||||
Name: "SA1017",
|
||||
Run: callChecker(checkUnbufferedSignalChanRules),
|
||||
Doc: Docs["SA1017"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1018": {
|
||||
Name: "SA1018",
|
||||
Run: callChecker(checkStringsReplaceZeroRules),
|
||||
Doc: Docs["SA1018"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1017": makeCallCheckerAnalyzer(checkUnbufferedSignalChanRules),
|
||||
"SA1018": makeCallCheckerAnalyzer(checkStringsReplaceZeroRules),
|
||||
"SA1019": {
|
||||
Name: "SA1019",
|
||||
Run: CheckDeprecated,
|
||||
Doc: Docs["SA1019"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Deprecated},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1020": {
|
||||
Name: "SA1020",
|
||||
Run: callChecker(checkListenAddressRules),
|
||||
Doc: Docs["SA1020"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1021": {
|
||||
Name: "SA1021",
|
||||
Run: callChecker(checkBytesEqualIPRules),
|
||||
Doc: Docs["SA1021"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Deprecated, facts.Generated},
|
||||
},
|
||||
"SA1020": makeCallCheckerAnalyzer(checkListenAddressRules),
|
||||
"SA1021": makeCallCheckerAnalyzer(checkBytesEqualIPRules),
|
||||
"SA1023": {
|
||||
Name: "SA1023",
|
||||
Run: CheckWriterBufferModified,
|
||||
Doc: Docs["SA1023"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1024": {
|
||||
Name: "SA1024",
|
||||
Run: callChecker(checkUniqueCutsetRules),
|
||||
Doc: Docs["SA1024"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1024": makeCallCheckerAnalyzer(checkUniqueCutsetRules),
|
||||
"SA1025": {
|
||||
Name: "SA1025",
|
||||
Run: CheckTimerResetReturnValue,
|
||||
Doc: Docs["SA1025"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1026": {
|
||||
Name: "SA1026",
|
||||
Run: callChecker(checkUnsupportedMarshal),
|
||||
Doc: Docs["SA1026"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA1027": {
|
||||
Name: "SA1027",
|
||||
Run: callChecker(checkAtomicAlignment),
|
||||
Doc: Docs["SA1027"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA1026": makeCallCheckerAnalyzer(checkUnsupportedMarshal),
|
||||
"SA1027": makeCallCheckerAnalyzer(checkAtomicAlignment),
|
||||
"SA1028": makeCallCheckerAnalyzer(checkSortSliceRules),
|
||||
"SA1029": makeCallCheckerAnalyzer(checkWithValueKeyRules),
|
||||
|
||||
"SA2000": {
|
||||
Name: "SA2000",
|
||||
Run: CheckWaitgroupAdd,
|
||||
Doc: Docs["SA2000"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA2001": {
|
||||
Name: "SA2001",
|
||||
Run: CheckEmptyCriticalSection,
|
||||
Doc: Docs["SA2001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA2002": {
|
||||
Name: "SA2002",
|
||||
Run: CheckConcurrentTesting,
|
||||
Doc: Docs["SA2002"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA2003": {
|
||||
Name: "SA2003",
|
||||
Run: CheckDeferLock,
|
||||
Doc: Docs["SA2003"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
|
||||
"SA3000": {
|
||||
Name: "SA3000",
|
||||
Run: CheckTestMainExit,
|
||||
Doc: Docs["SA3000"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA3001": {
|
||||
Name: "SA3001",
|
||||
Run: CheckBenchmarkN,
|
||||
Doc: Docs["SA3001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
|
||||
"SA4000": {
|
||||
Name: "SA4000",
|
||||
Run: CheckLhsRhsIdentical,
|
||||
Doc: Docs["SA4000"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.TokenFile, facts.Generated},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4001": {
|
||||
Name: "SA4001",
|
||||
Run: CheckIneffectiveCopy,
|
||||
Doc: Docs["SA4001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4002": {
|
||||
Name: "SA4002",
|
||||
Run: CheckDiffSizeComparison,
|
||||
Doc: Docs["SA4002"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4003": {
|
||||
Name: "SA4003",
|
||||
Run: CheckExtremeComparison,
|
||||
Doc: Docs["SA4003"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4004": {
|
||||
Name: "SA4004",
|
||||
Run: CheckIneffectiveLoop,
|
||||
Doc: Docs["SA4004"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4006": {
|
||||
Name: "SA4006",
|
||||
Run: CheckUnreadVariableValues,
|
||||
Doc: Docs["SA4006"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Generated},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.Generated},
|
||||
},
|
||||
"SA4008": {
|
||||
Name: "SA4008",
|
||||
Run: CheckLoopCondition,
|
||||
Doc: Docs["SA4008"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4009": {
|
||||
Name: "SA4009",
|
||||
Run: CheckArgOverwritten,
|
||||
Doc: Docs["SA4009"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4010": {
|
||||
Name: "SA4010",
|
||||
Run: CheckIneffectiveAppend,
|
||||
Doc: Docs["SA4010"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4011": {
|
||||
Name: "SA4011",
|
||||
Run: CheckScopedBreak,
|
||||
Doc: Docs["SA4011"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4012": {
|
||||
Name: "SA4012",
|
||||
Run: CheckNaNComparison,
|
||||
Doc: Docs["SA4012"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA4013": {
|
||||
Name: "SA4013",
|
||||
Run: CheckDoubleNegation,
|
||||
Doc: Docs["SA4013"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4014": {
|
||||
Name: "SA4014",
|
||||
Run: CheckRepeatedIfElse,
|
||||
Doc: Docs["SA4014"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4015": {
|
||||
Name: "SA4015",
|
||||
Run: callChecker(checkMathIntRules),
|
||||
Doc: Docs["SA4015"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4015": makeCallCheckerAnalyzer(checkMathIntRules),
|
||||
"SA4016": {
|
||||
Name: "SA4016",
|
||||
Run: CheckSillyBitwiseOps,
|
||||
Doc: Docs["SA4016"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.TokenFile},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.TokenFile},
|
||||
},
|
||||
"SA4017": {
|
||||
Name: "SA4017",
|
||||
Run: CheckPureFunctions,
|
||||
Doc: Docs["SA4017"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Purity},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.Purity},
|
||||
},
|
||||
"SA4018": {
|
||||
Name: "SA4018",
|
||||
Run: CheckSelfAssignment,
|
||||
Doc: Docs["SA4018"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile, facts.Purity},
|
||||
},
|
||||
"SA4019": {
|
||||
Name: "SA4019",
|
||||
Run: CheckDuplicateBuildConstraints,
|
||||
Doc: Docs["SA4019"].String(),
|
||||
Requires: []*analysis.Analyzer{facts.Generated},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4020": {
|
||||
Name: "SA4020",
|
||||
Run: CheckUnreachableTypeCases,
|
||||
Doc: Docs["SA4020"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA4021": {
|
||||
Name: "SA4021",
|
||||
Run: CheckSingleArgAppend,
|
||||
Doc: Docs["SA4021"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
|
||||
"SA5000": {
|
||||
Name: "SA5000",
|
||||
Run: CheckNilMaps,
|
||||
Doc: Docs["SA5000"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5001": {
|
||||
Name: "SA5001",
|
||||
Run: CheckEarlyDefer,
|
||||
Doc: Docs["SA5001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA5002": {
|
||||
Name: "SA5002",
|
||||
Run: CheckInfiniteEmptyLoop,
|
||||
Doc: Docs["SA5002"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA5003": {
|
||||
Name: "SA5003",
|
||||
Run: CheckDeferInInfiniteLoop,
|
||||
Doc: Docs["SA5003"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA5004": {
|
||||
Name: "SA5004",
|
||||
Run: CheckLoopEmptyDefault,
|
||||
Doc: Docs["SA5004"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA5005": {
|
||||
Name: "SA5005",
|
||||
Run: CheckCyclicFinalizer,
|
||||
Doc: Docs["SA5005"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5007": {
|
||||
Name: "SA5007",
|
||||
Run: CheckInfiniteRecursion,
|
||||
Doc: Docs["SA5007"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA5008": {
|
||||
Name: "SA5008",
|
||||
Run: CheckStructTags,
|
||||
Doc: Docs["SA5008"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA5009": {
|
||||
Name: "SA5009",
|
||||
Run: callChecker(checkPrintfRules),
|
||||
Doc: Docs["SA5009"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
"SA5009": makeCallCheckerAnalyzer(checkPrintfRules),
|
||||
"SA5010": {
|
||||
Run: CheckImpossibleTypeAssertion,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.TokenFile},
|
||||
},
|
||||
"SA5011": {
|
||||
Run: CheckMaybeNil,
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
|
||||
"SA6000": {
|
||||
Name: "SA6000",
|
||||
Run: callChecker(checkRegexpMatchLoopRules),
|
||||
Doc: Docs["SA6000"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA6000": makeCallCheckerAnalyzer(checkRegexpMatchLoopRules),
|
||||
"SA6001": {
|
||||
Name: "SA6001",
|
||||
Run: CheckMapBytesKey,
|
||||
Doc: Docs["SA6001"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA6002": {
|
||||
Name: "SA6002",
|
||||
Run: callChecker(checkSyncPoolValueRules),
|
||||
Doc: Docs["SA6002"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA6002": makeCallCheckerAnalyzer(checkSyncPoolValueRules),
|
||||
"SA6003": {
|
||||
Name: "SA6003",
|
||||
Run: CheckRangeStringRunes,
|
||||
Doc: Docs["SA6003"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
||||
},
|
||||
"SA6005": {
|
||||
Name: "SA6005",
|
||||
Run: CheckToLowerToUpperComparison,
|
||||
Doc: Docs["SA6005"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
|
||||
"SA9001": {
|
||||
Name: "SA9001",
|
||||
Run: CheckDubiousDeferInChannelRangeLoop,
|
||||
Doc: Docs["SA9001"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA9002": {
|
||||
Name: "SA9002",
|
||||
Run: CheckNonOctalFileMode,
|
||||
Doc: Docs["SA9002"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
"SA9003": {
|
||||
Name: "SA9003",
|
||||
Run: CheckEmptyBranch,
|
||||
Doc: Docs["SA9003"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.TokenFile, facts.Generated},
|
||||
Flags: newFlagSet(),
|
||||
Requires: []*analysis.Analyzer{buildir.Analyzer, facts.TokenFile, facts.Generated},
|
||||
},
|
||||
"SA9004": {
|
||||
Name: "SA9004",
|
||||
Run: CheckMissingEnumTypesInDeclaration,
|
||||
Doc: Docs["SA9004"].String(),
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Flags: newFlagSet(),
|
||||
},
|
||||
// Filtering generated code because it may include empty structs generated from data models.
|
||||
"SA9005": {
|
||||
Name: "SA9005",
|
||||
Run: callChecker(checkNoopMarshal),
|
||||
Doc: Docs["SA9005"].String(),
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer, facts.Generated, facts.TokenFile},
|
||||
Flags: newFlagSet(),
|
||||
"SA9005": makeCallCheckerAnalyzer(checkNoopMarshal, facts.Generated),
|
||||
|
||||
"SA4022": {
|
||||
Run: CheckAddressIsNil,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -4,12 +4,12 @@ import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
. "honnef.co/go/tools/lint/lintdsl"
|
||||
"honnef.co/go/tools/code"
|
||||
)
|
||||
|
||||
func buildTags(f *ast.File) [][]string {
|
||||
var out [][]string
|
||||
for _, line := range strings.Split(Preamble(f), "\n") {
|
||||
for _, line := range strings.Split(code.Preamble(f), "\n") {
|
||||
if !strings.HasPrefix(line, "+build ") {
|
||||
continue
|
||||
}
|
||||
|
264
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
264
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
@ -3,22 +3,22 @@ package staticcheck
|
||||
import "honnef.co/go/tools/lint"
|
||||
|
||||
var Docs = map[string]*lint.Documentation{
|
||||
"SA1000": &lint.Documentation{
|
||||
"SA1000": {
|
||||
Title: `Invalid regular expression`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1001": &lint.Documentation{
|
||||
"SA1001": {
|
||||
Title: `Invalid template`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1002": &lint.Documentation{
|
||||
"SA1002": {
|
||||
Title: `Invalid format in time.Parse`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1003": &lint.Documentation{
|
||||
"SA1003": {
|
||||
Title: `Unsupported argument to functions in encoding/binary`,
|
||||
Text: `The encoding/binary package can only serialize types with known sizes.
|
||||
This precludes the use of the int and uint types, as their sizes
|
||||
@ -29,7 +29,7 @@ Before Go 1.8, bool wasn't supported, either.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1004": &lint.Documentation{
|
||||
"SA1004": {
|
||||
Title: `Suspiciously small untyped constant in time.Sleep`,
|
||||
Text: `The time.Sleep function takes a time.Duration as its only argument.
|
||||
Durations are expressed in nanoseconds. Thus, calling time.Sleep(1)
|
||||
@ -41,12 +41,12 @@ large durations. These can be combined with arithmetic to express
|
||||
arbitrary durations, for example '5 * time.Second' for 5 seconds.
|
||||
|
||||
If you truly meant to sleep for a tiny amount of time, use
|
||||
'n * time.Nanosecond' to signal to staticcheck that you did mean to sleep
|
||||
'n * time.Nanosecond' to signal to Staticcheck that you did mean to sleep
|
||||
for some amount of nanoseconds.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1005": &lint.Documentation{
|
||||
"SA1005": {
|
||||
Title: `Invalid first argument to exec.Command`,
|
||||
Text: `os/exec runs programs directly (using variants of the fork and exec
|
||||
system calls on Unix systems). This shouldn't be confused with running
|
||||
@ -69,7 +69,7 @@ Windows, will have a /bin/sh program:
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1006": &lint.Documentation{
|
||||
"SA1006": {
|
||||
Title: `Printf with dynamic first argument and no further arguments`,
|
||||
Text: `Using fmt.Printf with a dynamic first argument can lead to unexpected
|
||||
output. The first argument is a format string, where certain character
|
||||
@ -93,12 +93,12 @@ and pass the string as an argument.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1007": &lint.Documentation{
|
||||
"SA1007": {
|
||||
Title: `Invalid URL in net/url.Parse`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1008": &lint.Documentation{
|
||||
"SA1008": {
|
||||
Title: `Non-canonical key in http.Header map`,
|
||||
Text: `Keys in http.Header maps are canonical, meaning they follow a specific
|
||||
combination of uppercase and lowercase letters. Methods such as
|
||||
@ -123,39 +123,39 @@ http.CanonicalHeaderKey.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1010": &lint.Documentation{
|
||||
"SA1010": {
|
||||
Title: `(*regexp.Regexp).FindAll called with n == 0, which will always return zero results`,
|
||||
Text: `If n >= 0, the function returns at most n matches/submatches. To
|
||||
return all results, specify a negative number.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1011": &lint.Documentation{
|
||||
"SA1011": {
|
||||
Title: `Various methods in the strings package expect valid UTF-8, but invalid input is provided`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1012": &lint.Documentation{
|
||||
"SA1012": {
|
||||
Title: `A nil context.Context is being passed to a function, consider using context.TODO instead`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1013": &lint.Documentation{
|
||||
"SA1013": {
|
||||
Title: `io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1014": &lint.Documentation{
|
||||
"SA1014": {
|
||||
Title: `Non-pointer value passed to Unmarshal or Decode`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1015": &lint.Documentation{
|
||||
"SA1015": {
|
||||
Title: `Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1016": &lint.Documentation{
|
||||
"SA1016": {
|
||||
Title: `Trapping a signal that cannot be trapped`,
|
||||
Text: `Not all signals can be intercepted by a process. Speficially, on
|
||||
UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are
|
||||
@ -164,7 +164,7 @@ kernel. It is therefore pointless to try and handle these signals.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1017": &lint.Documentation{
|
||||
"SA1017": {
|
||||
Title: `Channels used with os/signal.Notify should be buffered`,
|
||||
Text: `The os/signal package uses non-blocking channel sends when delivering
|
||||
signals. If the receiving end of the channel isn't ready and the
|
||||
@ -175,24 +175,24 @@ signal value, a buffer of size 1 is sufficient.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1018": &lint.Documentation{
|
||||
"SA1018": {
|
||||
Title: `strings.Replace called with n == 0, which does nothing`,
|
||||
Text: `With n == 0, zero instances will be replaced. To replace all
|
||||
instances, use a negative number, or use strings.ReplaceAll.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1019": &lint.Documentation{
|
||||
"SA1019": {
|
||||
Title: `Using a deprecated function, variable, constant or field`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1020": &lint.Documentation{
|
||||
"SA1020": {
|
||||
Title: `Using an invalid host:port pair with a net.Listen-related function`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1021": &lint.Documentation{
|
||||
"SA1021": {
|
||||
Title: `Using bytes.Equal to compare two net.IP`,
|
||||
Text: `A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The
|
||||
length of the slice for an IPv4 address, however, can be either 4 or
|
||||
@ -202,13 +202,13 @@ be used, as it takes both representations into account.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1023": &lint.Documentation{
|
||||
"SA1023": {
|
||||
Title: `Modifying the buffer in an io.Writer implementation`,
|
||||
Text: `Write must not modify the slice data, even temporarily.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1024": &lint.Documentation{
|
||||
"SA1024": {
|
||||
Title: `A string cutset contains duplicate characters`,
|
||||
Text: `The strings.TrimLeft and strings.TrimRight functions take cutsets, not
|
||||
prefixes. A cutset is treated as a set of characters to remove from a
|
||||
@ -223,17 +223,17 @@ In order to remove one string from another, use strings.TrimPrefix instead.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA1025": &lint.Documentation{
|
||||
"SA1025": {
|
||||
Title: `It is not possible to use (*time.Timer).Reset's return value correctly`,
|
||||
Since: "2019.1",
|
||||
},
|
||||
|
||||
"SA1026": &lint.Documentation{
|
||||
"SA1026": {
|
||||
Title: `Cannot marshal channels or functions`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA1027": &lint.Documentation{
|
||||
"SA1027": {
|
||||
Title: `Atomic access to 64-bit variable must be 64-bit aligned`,
|
||||
Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to
|
||||
arrange for 64-bit alignment of 64-bit words accessed atomically. The
|
||||
@ -245,12 +245,32 @@ in a struct.`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA2000": &lint.Documentation{
|
||||
"SA1028": {
|
||||
Title: `sort.Slice can only be used on slices`,
|
||||
Text: `The first argument of sort.Slice must be a slice.`,
|
||||
Since: "2020.1",
|
||||
},
|
||||
|
||||
"SA1029": {
|
||||
Title: `Inappropriate key in call to context.WithValue`,
|
||||
Text: `The provided key must be comparable and should not be
|
||||
of type string or any other built-in type to avoid collisions between
|
||||
packages using context. Users of WithValue should define their own
|
||||
types for keys.
|
||||
|
||||
To avoid allocating when assigning to an interface{},
|
||||
context keys often have concrete type struct{}. Alternatively,
|
||||
exported context key variables' static type should be a pointer or
|
||||
interface.`,
|
||||
Since: "2020.1",
|
||||
},
|
||||
|
||||
"SA2000": {
|
||||
Title: `sync.WaitGroup.Add called inside the goroutine, leading to a race condition`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA2001": &lint.Documentation{
|
||||
"SA2001": {
|
||||
Title: `Empty critical section, did you mean to defer the unlock?`,
|
||||
Text: `Empty critical sections of the kind
|
||||
|
||||
@ -271,17 +291,17 @@ rare false positive.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA2002": &lint.Documentation{
|
||||
"SA2002": {
|
||||
Title: `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA2003": &lint.Documentation{
|
||||
"SA2003": {
|
||||
Title: `Deferred Lock right after locking, likely meant to defer Unlock instead`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA3000": &lint.Documentation{
|
||||
"SA3000": {
|
||||
Title: `TestMain doesn't call os.Exit, hiding test failures`,
|
||||
Text: `Test executables (and in turn 'go test') exit with a non-zero status
|
||||
code if any tests failed. When specifying your own TestMain function,
|
||||
@ -292,7 +312,7 @@ os.Exit(m.Run()).`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA3001": &lint.Documentation{
|
||||
"SA3001": {
|
||||
Title: `Assigning to b.N in benchmarks distorts the results`,
|
||||
Text: `The testing package dynamically sets b.N to improve the reliability of
|
||||
benchmarks and uses it in computations to determine the duration of a
|
||||
@ -301,102 +321,102 @@ falsify results.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4000": &lint.Documentation{
|
||||
"SA4000": {
|
||||
Title: `Boolean expression has identical expressions on both sides`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4001": &lint.Documentation{
|
||||
"SA4001": {
|
||||
Title: `&*x gets simplified to x, it does not copy x`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4002": &lint.Documentation{
|
||||
"SA4002": {
|
||||
Title: `Comparing strings with known different sizes has predictable results`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4003": &lint.Documentation{
|
||||
"SA4003": {
|
||||
Title: `Comparing unsigned values against negative values is pointless`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4004": &lint.Documentation{
|
||||
"SA4004": {
|
||||
Title: `The loop exits unconditionally after one iteration`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4005": &lint.Documentation{
|
||||
"SA4005": {
|
||||
Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4006": &lint.Documentation{
|
||||
"SA4006": {
|
||||
Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4008": &lint.Documentation{
|
||||
"SA4008": {
|
||||
Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4009": &lint.Documentation{
|
||||
"SA4009": {
|
||||
Title: `A function argument is overwritten before its first use`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4010": &lint.Documentation{
|
||||
"SA4010": {
|
||||
Title: `The result of append will never be observed anywhere`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4011": &lint.Documentation{
|
||||
"SA4011": {
|
||||
Title: `Break statement with no effect. Did you mean to break out of an outer loop?`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4012": &lint.Documentation{
|
||||
"SA4012": {
|
||||
Title: `Comparing a value against NaN even though no value is equal to NaN`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4013": &lint.Documentation{
|
||||
"SA4013": {
|
||||
Title: `Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4014": &lint.Documentation{
|
||||
"SA4014": {
|
||||
Title: `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4015": &lint.Documentation{
|
||||
"SA4015": {
|
||||
Title: `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4016": &lint.Documentation{
|
||||
"SA4016": {
|
||||
Title: `Certain bitwise operations, such as x ^ 0, do not do anything useful`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4017": &lint.Documentation{
|
||||
"SA4017": {
|
||||
Title: `A pure function's return value is discarded, making the call pointless`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4018": &lint.Documentation{
|
||||
"SA4018": {
|
||||
Title: `Self-assignment of variables`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4019": &lint.Documentation{
|
||||
"SA4019": {
|
||||
Title: `Multiple, identical build constraints in the same file`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA4020": &lint.Documentation{
|
||||
"SA4020": {
|
||||
Title: `Unreachable case clause in a type switch`,
|
||||
Text: `In a type switch like the following
|
||||
|
||||
@ -467,27 +487,33 @@ and therefore doSomething()'s return value implements both.`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA4021": &lint.Documentation{
|
||||
"SA4021": {
|
||||
Title: `x = append(y) is equivalent to x = y`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA5000": &lint.Documentation{
|
||||
"SA4022": {
|
||||
Title: `Comparing the address of a variable against nil`,
|
||||
Text: `Code such as 'if &x == nil' is meaningless, because taking the address of a variable always yields a non-nil pointer.`,
|
||||
Since: "2020.1",
|
||||
},
|
||||
|
||||
"SA5000": {
|
||||
Title: `Assignment to nil map`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5001": &lint.Documentation{
|
||||
"SA5001": {
|
||||
Title: `Defering Close before checking for a possible error`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5002": &lint.Documentation{
|
||||
"SA5002": {
|
||||
Title: `The empty for loop (for {}) spins and can block the scheduler`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5003": &lint.Documentation{
|
||||
"SA5003": {
|
||||
Title: `Defers in infinite loops will never execute`,
|
||||
Text: `Defers are scoped to the surrounding function, not the surrounding
|
||||
block. In a function that never returns, i.e. one containing an
|
||||
@ -495,12 +521,12 @@ infinite loop, defers will never execute.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5004": &lint.Documentation{
|
||||
"SA5004": {
|
||||
Title: `for { select { ... with an empty default branch spins`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5005": &lint.Documentation{
|
||||
"SA5005": {
|
||||
Title: `The finalizer references the finalized object, preventing garbage collection`,
|
||||
Text: `A finalizer is a function associated with an object that runs when the
|
||||
garbage collector is ready to collect said object, that is when the
|
||||
@ -516,12 +542,12 @@ to zero before the object is being passed to the finalizer.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5006": &lint.Documentation{
|
||||
"SA5006": {
|
||||
Title: `Slice index out of bounds`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5007": &lint.Documentation{
|
||||
"SA5007": {
|
||||
Title: `Infinite recursive call`,
|
||||
Text: `A function that calls itself recursively needs to have an exit
|
||||
condition. Otherwise it will recurse forever, until the system runs
|
||||
@ -535,22 +561,112 @@ should be used instead.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA5008": &lint.Documentation{
|
||||
"SA5008": {
|
||||
Title: `Invalid struct tag`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA5009": &lint.Documentation{
|
||||
"SA5009": {
|
||||
Title: `Invalid Printf call`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA6000": &lint.Documentation{
|
||||
"SA5010": {
|
||||
Title: `Impossible type assertion`,
|
||||
|
||||
Text: `Some type assertions can be statically proven to be
|
||||
impossible. This is the case when the method sets of both
|
||||
arguments of the type assertion conflict with each other, for
|
||||
example by containing the same method with different
|
||||
signatures.
|
||||
|
||||
The Go compiler already applies this check when asserting from an
|
||||
interface value to a concrete type. If the concrete type misses
|
||||
methods from the interface, or if function signatures don't match,
|
||||
then the type assertion can never succeed.
|
||||
|
||||
This check applies the same logic when asserting from one interface to
|
||||
another. If both interface types contain the same method but with
|
||||
different signatures, then the type assertion can never succeed,
|
||||
either.`,
|
||||
|
||||
Since: "2020.1",
|
||||
},
|
||||
|
||||
"SA5011": {
|
||||
Title: `Possible nil pointer dereference`,
|
||||
|
||||
Text: `A pointer is being dereferenced unconditionally, while
|
||||
also being checked against nil in another place. This suggests that
|
||||
the pointer may be nil and dereferencing it may panic. This is
|
||||
commonly a result of improperly ordered code or missing return
|
||||
statements. Consider the following examples:
|
||||
|
||||
func fn(x *int) {
|
||||
fmt.Println(*x)
|
||||
|
||||
// This nil check is equally important for the previous dereference
|
||||
if x != nil {
|
||||
foo(*x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
x := compute()
|
||||
if x == nil {
|
||||
t.Errorf("nil pointer received")
|
||||
}
|
||||
|
||||
// t.Errorf does not abort the test, so if x is nil, the next line will panic.
|
||||
foo(*x)
|
||||
}
|
||||
|
||||
Staticcheck tries to deduce which functions abort control flow.
|
||||
For example, it is aware that a function will not continue
|
||||
execution after a call to panic or log.Fatal. However, sometimes
|
||||
this detection fails, in particular in the presence of
|
||||
conditionals. Consider the following example:
|
||||
|
||||
func Log(msg string, level int) {
|
||||
fmt.Println(msg)
|
||||
if level == levelFatal {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(msg string) {
|
||||
Log(msg, levelFatal)
|
||||
}
|
||||
|
||||
func fn(x *int) {
|
||||
if x == nil {
|
||||
Fatal("unexpected nil pointer")
|
||||
}
|
||||
fmt.Println(*x)
|
||||
}
|
||||
|
||||
Staticcheck will flag the dereference of x, even though it is perfectly
|
||||
safe. Staticcheck is not able to deduce that a call to
|
||||
Fatal will exit the program. For the time being, the easiest
|
||||
workaround is to modify the definition of Fatal like so:
|
||||
|
||||
func Fatal(msg string) {
|
||||
Log(msg, levelFatal)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
We also hard-code functions from common logging packages such as
|
||||
logrus. Please file an issue if we're missing support for a
|
||||
popular package.`,
|
||||
Since: "2020.1",
|
||||
},
|
||||
|
||||
"SA6000": {
|
||||
Title: `Using regexp.Match or related in a loop, should use regexp.Compile`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA6001": &lint.Documentation{
|
||||
"SA6001": {
|
||||
Title: `Missing an optimization opportunity when indexing maps by byte slices`,
|
||||
|
||||
Text: `Map keys must be comparable, which precludes the use of byte slices.
|
||||
@ -580,7 +696,7 @@ f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA6002": &lint.Documentation{
|
||||
"SA6002": {
|
||||
Title: `Storing non-pointer values in sync.Pool allocates memory`,
|
||||
Text: `A sync.Pool is used to avoid unnecessary allocations and reduce the
|
||||
amount of work the garbage collector has to do.
|
||||
@ -597,7 +713,7 @@ that discuss this problem.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA6003": &lint.Documentation{
|
||||
"SA6003": {
|
||||
Title: `Converting a string to a slice of runes before ranging over it`,
|
||||
Text: `You may want to loop over the runes in a string. Instead of converting
|
||||
the string to a slice of runes and looping over that, you can loop
|
||||
@ -619,7 +735,7 @@ the slice of runes.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA6005": &lint.Documentation{
|
||||
"SA6005": {
|
||||
Title: `Inefficient string comparison with strings.ToLower or strings.ToUpper`,
|
||||
Text: `Converting two strings to the same case and comparing them like so
|
||||
|
||||
@ -643,22 +759,22 @@ https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`,
|
||||
Since: "2019.2",
|
||||
},
|
||||
|
||||
"SA9001": &lint.Documentation{
|
||||
"SA9001": {
|
||||
Title: `Defers in range loops may not run when you expect them to`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA9002": &lint.Documentation{
|
||||
"SA9002": {
|
||||
Title: `Using a non-octal os.FileMode that looks like it was meant to be in octal.`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA9003": &lint.Documentation{
|
||||
"SA9003": {
|
||||
Title: `Empty body in an if or else branch`,
|
||||
Since: "2017.1",
|
||||
},
|
||||
|
||||
"SA9004": &lint.Documentation{
|
||||
"SA9004": {
|
||||
Title: `Only the first constant has an explicit type`,
|
||||
|
||||
Text: `In a constant declaration such as the following:
|
||||
@ -750,7 +866,7 @@ as EnumSecond has no explicit type, and thus defaults to int.`,
|
||||
Since: "2019.1",
|
||||
},
|
||||
|
||||
"SA9005": &lint.Documentation{
|
||||
"SA9005": {
|
||||
Title: `Trying to marshal a struct with no public fields nor custom marshaling`,
|
||||
Text: `The encoding/json and encoding/xml packages only operate on exported
|
||||
fields in structs, not unexported ones. It is usually an error to try
|
||||
|
@ -1,25 +0,0 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"honnef.co/go/tools/internal/passes/buildssa"
|
||||
"honnef.co/go/tools/ssa"
|
||||
"honnef.co/go/tools/staticcheck/vrp"
|
||||
)
|
||||
|
||||
var valueRangesAnalyzer = &analysis.Analyzer{
|
||||
Name: "vrp",
|
||||
Doc: "calculate value ranges of functions",
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
m := map[*ssa.Function]vrp.Ranges{}
|
||||
for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
|
||||
vr := vrp.BuildGraph(ssafn).Solve()
|
||||
m[ssafn] = vr
|
||||
}
|
||||
return m, nil
|
||||
},
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
ResultType: reflect.TypeOf(map[*ssa.Function]vrp.Ranges{}),
|
||||
}
|
1639
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
1639
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
File diff suppressed because it is too large
Load Diff
72
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
72
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
@ -14,9 +14,8 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
. "honnef.co/go/tools/lint/lintdsl"
|
||||
"honnef.co/go/tools/ssa"
|
||||
"honnef.co/go/tools/staticcheck/vrp"
|
||||
"honnef.co/go/tools/code"
|
||||
"honnef.co/go/tools/ir"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -27,10 +26,10 @@ const (
|
||||
|
||||
type Call struct {
|
||||
Pass *analysis.Pass
|
||||
Instr ssa.CallInstruction
|
||||
Instr ir.CallInstruction
|
||||
Args []*Argument
|
||||
|
||||
Parent *ssa.Function
|
||||
Parent *ir.Function
|
||||
|
||||
invalids []string
|
||||
}
|
||||
@ -44,22 +43,21 @@ type Argument struct {
|
||||
invalids []string
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Value ir.Value
|
||||
}
|
||||
|
||||
func (arg *Argument) Invalid(msg string) {
|
||||
arg.invalids = append(arg.invalids, msg)
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Value ssa.Value
|
||||
Range vrp.Range
|
||||
}
|
||||
|
||||
type CallCheck func(call *Call)
|
||||
|
||||
func extractConsts(v ssa.Value) []*ssa.Const {
|
||||
func extractConsts(v ir.Value) []*ir.Const {
|
||||
switch v := v.(type) {
|
||||
case *ssa.Const:
|
||||
return []*ssa.Const{v}
|
||||
case *ssa.MakeInterface:
|
||||
case *ir.Const:
|
||||
return []*ir.Const{v}
|
||||
case *ir.MakeInterface:
|
||||
return extractConsts(v.X)
|
||||
default:
|
||||
return nil
|
||||
@ -118,20 +116,6 @@ func ValidateURL(v Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func IntValue(v Value, z vrp.Z) bool {
|
||||
r, ok := v.Range.(vrp.IntInterval)
|
||||
if !ok || !r.IsKnown() {
|
||||
return false
|
||||
}
|
||||
if r.Lower != r.Upper {
|
||||
return false
|
||||
}
|
||||
if r.Lower.Cmp(z) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func InvalidUTF8(v Value) bool {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
@ -149,13 +133,21 @@ func InvalidUTF8(v Value) bool {
|
||||
}
|
||||
|
||||
func UnbufferedChannel(v Value) bool {
|
||||
r, ok := v.Range.(vrp.ChannelInterval)
|
||||
if !ok || !r.IsKnown() {
|
||||
// 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 r.Size.Lower.Cmp(vrp.NewZ(0)) == 0 &&
|
||||
r.Size.Upper.Cmp(vrp.NewZ(0)) == 0 {
|
||||
return true
|
||||
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
|
||||
}
|
||||
@ -169,7 +161,7 @@ func Pointer(v Value) bool {
|
||||
}
|
||||
|
||||
func ConvertedFromInt(v Value) bool {
|
||||
conv, ok := v.Value.(*ssa.Convert)
|
||||
conv, ok := v.Value.(*ir.Convert)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@ -193,7 +185,7 @@ func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool {
|
||||
types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
|
||||
return true
|
||||
case types.Bool:
|
||||
return IsGoVersion(pass, 8)
|
||||
return code.IsGoVersion(pass, 8)
|
||||
}
|
||||
return false
|
||||
case *types.Struct:
|
||||
@ -232,8 +224,10 @@ func CanBinaryMarshal(pass *analysis.Pass, v Value) bool {
|
||||
func RepeatZeroTimes(name string, arg int) CallCheck {
|
||||
return func(call *Call) {
|
||||
arg := call.Args[arg]
|
||||
if IntValue(arg.Value, vrp.NewZ(0)) {
|
||||
arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name))
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,8 +287,8 @@ func ValidHostPort(v Value) bool {
|
||||
|
||||
// ConvertedFrom reports whether value v was converted from type typ.
|
||||
func ConvertedFrom(v Value, typ string) bool {
|
||||
change, ok := v.Value.(*ssa.ChangeType)
|
||||
return ok && IsType(change.X.Type(), typ)
|
||||
change, ok := v.Value.(*ir.ChangeType)
|
||||
return ok && code.IsType(change.X.Type(), typ)
|
||||
}
|
||||
|
||||
func UniqueStringCutset(v Value) bool {
|
||||
|
@ -1,73 +0,0 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type ChannelInterval struct {
|
||||
Size IntInterval
|
||||
}
|
||||
|
||||
func (c ChannelInterval) Union(other Range) Range {
|
||||
i, ok := other.(ChannelInterval)
|
||||
if !ok {
|
||||
i = ChannelInterval{EmptyIntInterval}
|
||||
}
|
||||
if c.Size.Empty() || !c.Size.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Size.Empty() || !i.Size.IsKnown() {
|
||||
return c
|
||||
}
|
||||
return ChannelInterval{
|
||||
Size: c.Size.Union(i.Size).(IntInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (c ChannelInterval) String() string {
|
||||
return c.Size.String()
|
||||
}
|
||||
|
||||
func (c ChannelInterval) IsKnown() bool {
|
||||
return c.Size.IsKnown()
|
||||
}
|
||||
|
||||
type MakeChannelConstraint struct {
|
||||
aConstraint
|
||||
Buffer ssa.Value
|
||||
}
|
||||
type ChannelChangeTypeConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
func NewMakeChannelConstraint(buffer, y ssa.Value) Constraint {
|
||||
return &MakeChannelConstraint{NewConstraint(y), buffer}
|
||||
}
|
||||
func NewChannelChangeTypeConstraint(x, y ssa.Value) Constraint {
|
||||
return &ChannelChangeTypeConstraint{NewConstraint(y), x}
|
||||
}
|
||||
|
||||
func (c *MakeChannelConstraint) Operands() []ssa.Value { return []ssa.Value{c.Buffer} }
|
||||
func (c *ChannelChangeTypeConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
|
||||
func (c *MakeChannelConstraint) String() string {
|
||||
return fmt.Sprintf("%s = make(chan, %s)", c.Y().Name(), c.Buffer.Name())
|
||||
}
|
||||
func (c *ChannelChangeTypeConstraint) String() string {
|
||||
return fmt.Sprintf("%s = changetype(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
|
||||
func (c *MakeChannelConstraint) Eval(g *Graph) Range {
|
||||
i, ok := g.Range(c.Buffer).(IntInterval)
|
||||
if !ok {
|
||||
return ChannelInterval{NewIntInterval(NewZ(0), PInfinity)}
|
||||
}
|
||||
if i.Lower.Sign() == -1 {
|
||||
i.Lower = NewZ(0)
|
||||
}
|
||||
return ChannelInterval{i}
|
||||
}
|
||||
func (c *ChannelChangeTypeConstraint) Eval(g *Graph) Range { return g.Range(c.X) }
|
476
vendor/honnef.co/go/tools/staticcheck/vrp/int.go
vendored
476
vendor/honnef.co/go/tools/staticcheck/vrp/int.go
vendored
@ -1,476 +0,0 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type Zs []Z
|
||||
|
||||
func (zs Zs) Len() int {
|
||||
return len(zs)
|
||||
}
|
||||
|
||||
func (zs Zs) Less(i int, j int) bool {
|
||||
return zs[i].Cmp(zs[j]) == -1
|
||||
}
|
||||
|
||||
func (zs Zs) Swap(i int, j int) {
|
||||
zs[i], zs[j] = zs[j], zs[i]
|
||||
}
|
||||
|
||||
type Z struct {
|
||||
infinity int8
|
||||
integer *big.Int
|
||||
}
|
||||
|
||||
func NewZ(n int64) Z {
|
||||
return NewBigZ(big.NewInt(n))
|
||||
}
|
||||
|
||||
func NewBigZ(n *big.Int) Z {
|
||||
return Z{integer: n}
|
||||
}
|
||||
|
||||
func (z1 Z) Infinite() bool {
|
||||
return z1.infinity != 0
|
||||
}
|
||||
|
||||
func (z1 Z) Add(z2 Z) Z {
|
||||
if z2.Sign() == -1 {
|
||||
return z1.Sub(z2.Negate())
|
||||
}
|
||||
if z1 == NInfinity {
|
||||
return NInfinity
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
if z2 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
|
||||
if !z1.Infinite() && !z2.Infinite() {
|
||||
n := &big.Int{}
|
||||
n.Add(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%s + %s is not defined", z1, z2))
|
||||
}
|
||||
|
||||
func (z1 Z) Sub(z2 Z) Z {
|
||||
if z2.Sign() == -1 {
|
||||
return z1.Add(z2.Negate())
|
||||
}
|
||||
if !z1.Infinite() && !z2.Infinite() {
|
||||
n := &big.Int{}
|
||||
n.Sub(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
if z1 != PInfinity && z2 == PInfinity {
|
||||
return NInfinity
|
||||
}
|
||||
if z1.Infinite() && !z2.Infinite() {
|
||||
return Z{infinity: z1.infinity}
|
||||
}
|
||||
if z1 == PInfinity && z2 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
panic(fmt.Sprintf("%s - %s is not defined", z1, z2))
|
||||
}
|
||||
|
||||
func (z1 Z) Mul(z2 Z) Z {
|
||||
if (z1.integer != nil && z1.integer.Sign() == 0) ||
|
||||
(z2.integer != nil && z2.integer.Sign() == 0) {
|
||||
return NewBigZ(&big.Int{})
|
||||
}
|
||||
|
||||
if z1.infinity != 0 || z2.infinity != 0 {
|
||||
return Z{infinity: int8(z1.Sign() * z2.Sign())}
|
||||
}
|
||||
|
||||
n := &big.Int{}
|
||||
n.Mul(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
func (z1 Z) Negate() Z {
|
||||
if z1.infinity == 1 {
|
||||
return NInfinity
|
||||
}
|
||||
if z1.infinity == -1 {
|
||||
return PInfinity
|
||||
}
|
||||
n := &big.Int{}
|
||||
n.Neg(z1.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
func (z1 Z) Sign() int {
|
||||
if z1.infinity != 0 {
|
||||
return int(z1.infinity)
|
||||
}
|
||||
return z1.integer.Sign()
|
||||
}
|
||||
|
||||
func (z1 Z) String() string {
|
||||
if z1 == NInfinity {
|
||||
return "-∞"
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return "∞"
|
||||
}
|
||||
return fmt.Sprintf("%d", z1.integer)
|
||||
}
|
||||
|
||||
func (z1 Z) Cmp(z2 Z) int {
|
||||
if z1.infinity == z2.infinity && z1.infinity != 0 {
|
||||
return 0
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return 1
|
||||
}
|
||||
if z1 == NInfinity {
|
||||
return -1
|
||||
}
|
||||
if z2 == NInfinity {
|
||||
return 1
|
||||
}
|
||||
if z2 == PInfinity {
|
||||
return -1
|
||||
}
|
||||
return z1.integer.Cmp(z2.integer)
|
||||
}
|
||||
|
||||
func MaxZ(zs ...Z) Z {
|
||||
if len(zs) == 0 {
|
||||
panic("Max called with no arguments")
|
||||
}
|
||||
if len(zs) == 1 {
|
||||
return zs[0]
|
||||
}
|
||||
ret := zs[0]
|
||||
for _, z := range zs[1:] {
|
||||
if z.Cmp(ret) == 1 {
|
||||
ret = z
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func MinZ(zs ...Z) Z {
|
||||
if len(zs) == 0 {
|
||||
panic("Min called with no arguments")
|
||||
}
|
||||
if len(zs) == 1 {
|
||||
return zs[0]
|
||||
}
|
||||
ret := zs[0]
|
||||
for _, z := range zs[1:] {
|
||||
if z.Cmp(ret) == -1 {
|
||||
ret = z
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var NInfinity = Z{infinity: -1}
|
||||
var PInfinity = Z{infinity: 1}
|
||||
var EmptyIntInterval = IntInterval{true, PInfinity, NInfinity}
|
||||
|
||||
func InfinityFor(v ssa.Value) IntInterval {
|
||||
if b, ok := v.Type().Underlying().(*types.Basic); ok {
|
||||
if (b.Info() & types.IsUnsigned) != 0 {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
}
|
||||
return NewIntInterval(NInfinity, PInfinity)
|
||||
}
|
||||
|
||||
type IntInterval struct {
|
||||
known bool
|
||||
Lower Z
|
||||
Upper Z
|
||||
}
|
||||
|
||||
func NewIntInterval(l, u Z) IntInterval {
|
||||
if u.Cmp(l) == -1 {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
return IntInterval{known: true, Lower: l, Upper: u}
|
||||
}
|
||||
|
||||
func (i IntInterval) IsKnown() bool {
|
||||
return i.known
|
||||
}
|
||||
|
||||
func (i IntInterval) Empty() bool {
|
||||
return i.Lower == PInfinity && i.Upper == NInfinity
|
||||
}
|
||||
|
||||
func (i IntInterval) IsMaxRange() bool {
|
||||
return i.Lower == NInfinity && i.Upper == PInfinity
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Intersection(i2 IntInterval) IntInterval {
|
||||
if !i1.IsKnown() {
|
||||
return i2
|
||||
}
|
||||
if !i2.IsKnown() {
|
||||
return i1
|
||||
}
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
i3 := NewIntInterval(MaxZ(i1.Lower, i2.Lower), MinZ(i1.Upper, i2.Upper))
|
||||
if i3.Lower.Cmp(i3.Upper) == 1 {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
return i3
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Union(other Range) Range {
|
||||
i2, ok := other.(IntInterval)
|
||||
if !ok {
|
||||
i2 = EmptyIntInterval
|
||||
}
|
||||
if i1.Empty() || !i1.IsKnown() {
|
||||
return i2
|
||||
}
|
||||
if i2.Empty() || !i2.IsKnown() {
|
||||
return i1
|
||||
}
|
||||
return NewIntInterval(MinZ(i1.Lower, i2.Lower), MaxZ(i1.Upper, i2.Upper))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Add(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
|
||||
return NewIntInterval(l1.Add(l2), u1.Add(u2))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Sub(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
|
||||
return NewIntInterval(l1.Sub(u2), u1.Sub(l2))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Mul(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
x1, x2 := i1.Lower, i1.Upper
|
||||
y1, y2 := i2.Lower, i2.Upper
|
||||
return NewIntInterval(
|
||||
MinZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
|
||||
MaxZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
|
||||
)
|
||||
}
|
||||
|
||||
func (i1 IntInterval) String() string {
|
||||
if !i1.IsKnown() {
|
||||
return "[⊥, ⊥]"
|
||||
}
|
||||
if i1.Empty() {
|
||||
return "{}"
|
||||
}
|
||||
return fmt.Sprintf("[%s, %s]", i1.Lower, i1.Upper)
|
||||
}
|
||||
|
||||
type IntArithmeticConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
Fn func(IntInterval, IntInterval) IntInterval
|
||||
}
|
||||
|
||||
type IntAddConstraint struct{ *IntArithmeticConstraint }
|
||||
type IntSubConstraint struct{ *IntArithmeticConstraint }
|
||||
type IntMulConstraint struct{ *IntArithmeticConstraint }
|
||||
|
||||
type IntConversionConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type IntIntersectionConstraint struct {
|
||||
aConstraint
|
||||
ranges Ranges
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
I IntInterval
|
||||
resolved bool
|
||||
}
|
||||
|
||||
type IntIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewIntArithmeticConstraint(a, b, y ssa.Value, op token.Token, fn func(IntInterval, IntInterval) IntInterval) *IntArithmeticConstraint {
|
||||
return &IntArithmeticConstraint{NewConstraint(y), a, b, op, fn}
|
||||
}
|
||||
func NewIntAddConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntAddConstraint{NewIntArithmeticConstraint(a, b, y, token.ADD, IntInterval.Add)}
|
||||
}
|
||||
func NewIntSubConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntSubConstraint{NewIntArithmeticConstraint(a, b, y, token.SUB, IntInterval.Sub)}
|
||||
}
|
||||
func NewIntMulConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntMulConstraint{NewIntArithmeticConstraint(a, b, y, token.MUL, IntInterval.Mul)}
|
||||
}
|
||||
func NewIntConversionConstraint(x, y ssa.Value) Constraint {
|
||||
return &IntConversionConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewIntIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
|
||||
return &IntIntersectionConstraint{
|
||||
aConstraint: NewConstraint(y),
|
||||
ranges: ranges,
|
||||
A: a,
|
||||
B: b,
|
||||
Op: op,
|
||||
}
|
||||
}
|
||||
func NewIntIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &IntIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *IntArithmeticConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *IntConversionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *IntIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
|
||||
func (s *IntIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *IntArithmeticConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s", c.Y().Name(), c.A.Name(), c.Op, c.B.Name())
|
||||
}
|
||||
func (c *IntConversionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s(%s)", c.Y().Name(), c.Y().Type(), c.X.Name())
|
||||
}
|
||||
func (c *IntIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
|
||||
}
|
||||
func (c *IntIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *IntArithmeticConstraint) Eval(g *Graph) Range {
|
||||
i1, i2 := g.Range(c.A).(IntInterval), g.Range(c.B).(IntInterval)
|
||||
if !i1.IsKnown() || !i2.IsKnown() {
|
||||
return IntInterval{}
|
||||
}
|
||||
return c.Fn(i1, i2)
|
||||
}
|
||||
func (c *IntConversionConstraint) Eval(g *Graph) Range {
|
||||
s := &types.StdSizes{
|
||||
// XXX is it okay to assume the largest word size, or do we
|
||||
// need to be platform specific?
|
||||
WordSize: 8,
|
||||
MaxAlign: 1,
|
||||
}
|
||||
fromI := g.Range(c.X).(IntInterval)
|
||||
toI := g.Range(c.Y()).(IntInterval)
|
||||
fromT := c.X.Type().Underlying().(*types.Basic)
|
||||
toT := c.Y().Type().Underlying().(*types.Basic)
|
||||
fromB := s.Sizeof(c.X.Type())
|
||||
toB := s.Sizeof(c.Y().Type())
|
||||
|
||||
if !fromI.IsKnown() {
|
||||
return toI
|
||||
}
|
||||
if !toI.IsKnown() {
|
||||
return fromI
|
||||
}
|
||||
|
||||
// uint<N> -> sint/uint<M>, M > N: [max(0, l1), min(2**N-1, u2)]
|
||||
if (fromT.Info()&types.IsUnsigned != 0) &&
|
||||
toB > fromB {
|
||||
|
||||
n := big.NewInt(1)
|
||||
n.Lsh(n, uint(fromB*8))
|
||||
n.Sub(n, big.NewInt(1))
|
||||
return NewIntInterval(
|
||||
MaxZ(NewZ(0), fromI.Lower),
|
||||
MinZ(NewBigZ(n), toI.Upper),
|
||||
)
|
||||
}
|
||||
|
||||
// sint<N> -> sint<M>, M > N; [max(-∞, l1), min(2**N-1, u2)]
|
||||
if (fromT.Info()&types.IsUnsigned == 0) &&
|
||||
(toT.Info()&types.IsUnsigned == 0) &&
|
||||
toB > fromB {
|
||||
|
||||
n := big.NewInt(1)
|
||||
n.Lsh(n, uint(fromB*8))
|
||||
n.Sub(n, big.NewInt(1))
|
||||
return NewIntInterval(
|
||||
MaxZ(NInfinity, fromI.Lower),
|
||||
MinZ(NewBigZ(n), toI.Upper),
|
||||
)
|
||||
}
|
||||
|
||||
return fromI
|
||||
}
|
||||
func (c *IntIntersectionConstraint) Eval(g *Graph) Range {
|
||||
xi := g.Range(c.A).(IntInterval)
|
||||
if !xi.IsKnown() {
|
||||
return c.I
|
||||
}
|
||||
return xi.Intersection(c.I)
|
||||
}
|
||||
func (c *IntIntervalConstraint) Eval(*Graph) Range { return c.I }
|
||||
|
||||
func (c *IntIntersectionConstraint) Futures() []ssa.Value {
|
||||
return []ssa.Value{c.B}
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) Resolve() {
|
||||
r, ok := c.ranges[c.B].(IntInterval)
|
||||
if !ok {
|
||||
c.I = InfinityFor(c.Y())
|
||||
return
|
||||
}
|
||||
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r
|
||||
case token.GTR:
|
||||
c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
|
||||
case token.GEQ:
|
||||
c.I = NewIntInterval(r.Lower, PInfinity)
|
||||
case token.LSS:
|
||||
// TODO(dh): do we need 0 instead of NInfinity for uints?
|
||||
c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
|
||||
case token.LEQ:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper)
|
||||
case token.NEQ:
|
||||
c.I = InfinityFor(c.Y())
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) IsKnown() bool {
|
||||
return c.I.IsKnown()
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) MarkUnresolved() {
|
||||
c.resolved = false
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) MarkResolved() {
|
||||
c.resolved = true
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) IsResolved() bool {
|
||||
return c.resolved
|
||||
}
|
273
vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
vendored
273
vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
vendored
@ -1,273 +0,0 @@
|
||||
package vrp
|
||||
|
||||
// TODO(dh): most of the constraints have implementations identical to
|
||||
// that of strings. Consider reusing them.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type SliceInterval struct {
|
||||
Length IntInterval
|
||||
}
|
||||
|
||||
func (s SliceInterval) Union(other Range) Range {
|
||||
i, ok := other.(SliceInterval)
|
||||
if !ok {
|
||||
i = SliceInterval{EmptyIntInterval}
|
||||
}
|
||||
if s.Length.Empty() || !s.Length.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Length.Empty() || !i.Length.IsKnown() {
|
||||
return s
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: s.Length.Union(i.Length).(IntInterval),
|
||||
}
|
||||
}
|
||||
func (s SliceInterval) String() string { return s.Length.String() }
|
||||
func (s SliceInterval) IsKnown() bool { return s.Length.IsKnown() }
|
||||
|
||||
type SliceAppendConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
}
|
||||
|
||||
type SliceSliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type ArraySliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type SliceIntersectionConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
type SliceLengthConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type MakeSliceConstraint struct {
|
||||
aConstraint
|
||||
Size ssa.Value
|
||||
}
|
||||
|
||||
type SliceIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewSliceAppendConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &SliceAppendConstraint{NewConstraint(y), a, b}
|
||||
}
|
||||
func NewSliceSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &SliceSliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewArraySliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &ArraySliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewSliceIntersectionConstraint(x ssa.Value, i IntInterval, y ssa.Value) Constraint {
|
||||
return &SliceIntersectionConstraint{NewConstraint(y), x, i}
|
||||
}
|
||||
func NewSliceLengthConstraint(x, y ssa.Value) Constraint {
|
||||
return &SliceLengthConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewMakeSliceConstraint(size, y ssa.Value) Constraint {
|
||||
return &MakeSliceConstraint{NewConstraint(y), size}
|
||||
}
|
||||
func NewSliceIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &SliceIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *SliceAppendConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *SliceSliceConstraint) Operands() []ssa.Value {
|
||||
ops := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
ops = append(ops, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
ops = append(ops, c.Upper)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
func (c *ArraySliceConstraint) Operands() []ssa.Value {
|
||||
ops := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
ops = append(ops, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
ops = append(ops, c.Upper)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *SliceLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *MakeSliceConstraint) Operands() []ssa.Value { return []ssa.Value{c.Size} }
|
||||
func (s *SliceIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *SliceAppendConstraint) String() string {
|
||||
return fmt.Sprintf("%s = append(%s, %s)", c.Y().Name(), c.A.Name(), c.B.Name())
|
||||
}
|
||||
func (c *SliceSliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *ArraySliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s.%t ⊓ %s", c.Y().Name(), c.X.Name(), c.Y().(*ssa.Sigma).Branch, c.I)
|
||||
}
|
||||
func (c *SliceLengthConstraint) String() string {
|
||||
return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
func (c *MakeSliceConstraint) String() string {
|
||||
return fmt.Sprintf("%s = make(slice, %s)", c.Y().Name(), c.Size.Name())
|
||||
}
|
||||
func (c *SliceIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *SliceAppendConstraint) Eval(g *Graph) Range {
|
||||
l1 := g.Range(c.A).(SliceInterval).Length
|
||||
var l2 IntInterval
|
||||
switch r := g.Range(c.B).(type) {
|
||||
case SliceInterval:
|
||||
l2 = r.Length
|
||||
case StringInterval:
|
||||
l2 = r.Length
|
||||
default:
|
||||
return SliceInterval{}
|
||||
}
|
||||
if !l1.IsKnown() || !l2.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: l1.Add(l2),
|
||||
}
|
||||
}
|
||||
func (c *SliceSliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
ur := g.Range(c.X).(SliceInterval).Length
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return SliceInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *ArraySliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
var l int64
|
||||
switch typ := c.X.Type().(type) {
|
||||
case *types.Array:
|
||||
l = typ.Len()
|
||||
case *types.Pointer:
|
||||
l = typ.Elem().(*types.Array).Len()
|
||||
}
|
||||
ur := NewIntInterval(NewZ(l), NewZ(l))
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return SliceInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) Eval(g *Graph) Range {
|
||||
xi := g.Range(c.X).(SliceInterval)
|
||||
if !xi.IsKnown() {
|
||||
return c.I
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: xi.Length.Intersection(c.I),
|
||||
}
|
||||
}
|
||||
func (c *SliceLengthConstraint) Eval(g *Graph) Range {
|
||||
i := g.Range(c.X).(SliceInterval).Length
|
||||
if !i.IsKnown() {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
return i
|
||||
}
|
||||
func (c *MakeSliceConstraint) Eval(g *Graph) Range {
|
||||
i, ok := g.Range(c.Size).(IntInterval)
|
||||
if !ok {
|
||||
return SliceInterval{NewIntInterval(NewZ(0), PInfinity)}
|
||||
}
|
||||
if i.Lower.Sign() == -1 {
|
||||
i.Lower = NewZ(0)
|
||||
}
|
||||
return SliceInterval{i}
|
||||
}
|
||||
func (c *SliceIntervalConstraint) Eval(*Graph) Range { return SliceInterval{c.I} }
|
258
vendor/honnef.co/go/tools/staticcheck/vrp/string.go
vendored
258
vendor/honnef.co/go/tools/staticcheck/vrp/string.go
vendored
@ -1,258 +0,0 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type StringInterval struct {
|
||||
Length IntInterval
|
||||
}
|
||||
|
||||
func (s StringInterval) Union(other Range) Range {
|
||||
i, ok := other.(StringInterval)
|
||||
if !ok {
|
||||
i = StringInterval{EmptyIntInterval}
|
||||
}
|
||||
if s.Length.Empty() || !s.Length.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Length.Empty() || !i.Length.IsKnown() {
|
||||
return s
|
||||
}
|
||||
return StringInterval{
|
||||
Length: s.Length.Union(i.Length).(IntInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (s StringInterval) String() string {
|
||||
return s.Length.String()
|
||||
}
|
||||
|
||||
func (s StringInterval) IsKnown() bool {
|
||||
return s.Length.IsKnown()
|
||||
}
|
||||
|
||||
type StringSliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type StringIntersectionConstraint struct {
|
||||
aConstraint
|
||||
ranges Ranges
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
I IntInterval
|
||||
resolved bool
|
||||
}
|
||||
|
||||
type StringConcatConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
}
|
||||
|
||||
type StringLengthConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type StringIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewStringSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &StringSliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewStringIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
|
||||
return &StringIntersectionConstraint{
|
||||
aConstraint: NewConstraint(y),
|
||||
ranges: ranges,
|
||||
A: a,
|
||||
B: b,
|
||||
Op: op,
|
||||
}
|
||||
}
|
||||
func NewStringConcatConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &StringConcatConstraint{NewConstraint(y), a, b}
|
||||
}
|
||||
func NewStringLengthConstraint(x ssa.Value, y ssa.Value) Constraint {
|
||||
return &StringLengthConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewStringIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &StringIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *StringSliceConstraint) Operands() []ssa.Value {
|
||||
vs := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
vs = append(vs, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
vs = append(vs, c.Upper)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
func (c *StringIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
|
||||
func (c StringConcatConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *StringLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (s *StringIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *StringSliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *StringIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
|
||||
}
|
||||
func (c StringConcatConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s + %s", c.Y().Name(), c.A.Name(), c.B.Name())
|
||||
}
|
||||
func (c *StringLengthConstraint) String() string {
|
||||
return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
func (c *StringIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *StringSliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
ur := g.Range(c.X).(StringInterval).Length
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return StringInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return StringInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *StringIntersectionConstraint) Eval(g *Graph) Range {
|
||||
var l IntInterval
|
||||
switch r := g.Range(c.A).(type) {
|
||||
case StringInterval:
|
||||
l = r.Length
|
||||
case IntInterval:
|
||||
l = r
|
||||
}
|
||||
|
||||
if !l.IsKnown() {
|
||||
return StringInterval{c.I}
|
||||
}
|
||||
return StringInterval{
|
||||
Length: l.Intersection(c.I),
|
||||
}
|
||||
}
|
||||
func (c StringConcatConstraint) Eval(g *Graph) Range {
|
||||
i1, i2 := g.Range(c.A).(StringInterval), g.Range(c.B).(StringInterval)
|
||||
if !i1.Length.IsKnown() || !i2.Length.IsKnown() {
|
||||
return StringInterval{}
|
||||
}
|
||||
return StringInterval{
|
||||
Length: i1.Length.Add(i2.Length),
|
||||
}
|
||||
}
|
||||
func (c *StringLengthConstraint) Eval(g *Graph) Range {
|
||||
i := g.Range(c.X).(StringInterval).Length
|
||||
if !i.IsKnown() {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
return i
|
||||
}
|
||||
func (c *StringIntervalConstraint) Eval(*Graph) Range { return StringInterval{c.I} }
|
||||
|
||||
func (c *StringIntersectionConstraint) Futures() []ssa.Value {
|
||||
return []ssa.Value{c.B}
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) Resolve() {
|
||||
if (c.A.Type().Underlying().(*types.Basic).Info() & types.IsString) != 0 {
|
||||
// comparing two strings
|
||||
r, ok := c.ranges[c.B].(StringInterval)
|
||||
if !ok {
|
||||
c.I = NewIntInterval(NewZ(0), PInfinity)
|
||||
return
|
||||
}
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r.Length
|
||||
case token.GTR, token.GEQ:
|
||||
c.I = NewIntInterval(r.Length.Lower, PInfinity)
|
||||
case token.LSS, token.LEQ:
|
||||
c.I = NewIntInterval(NewZ(0), r.Length.Upper)
|
||||
case token.NEQ:
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
} else {
|
||||
r, ok := c.ranges[c.B].(IntInterval)
|
||||
if !ok {
|
||||
c.I = NewIntInterval(NewZ(0), PInfinity)
|
||||
return
|
||||
}
|
||||
// comparing two lengths
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r
|
||||
case token.GTR:
|
||||
c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
|
||||
case token.GEQ:
|
||||
c.I = NewIntInterval(r.Lower, PInfinity)
|
||||
case token.LSS:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
|
||||
case token.LEQ:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper)
|
||||
case token.NEQ:
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) IsKnown() bool {
|
||||
return c.I.IsKnown()
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) MarkUnresolved() {
|
||||
c.resolved = false
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) MarkResolved() {
|
||||
c.resolved = true
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) IsResolved() bool {
|
||||
return c.resolved
|
||||
}
|
1056
vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
vendored
1056
vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user