github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1024/sa1024.go (about) 1 package sa1024 2 3 import ( 4 "go/constant" 5 "sort" 6 7 "github.com/amarpal/go-tools/analysis/callcheck" 8 "github.com/amarpal/go-tools/analysis/lint" 9 "github.com/amarpal/go-tools/internal/passes/buildir" 10 11 "golang.org/x/tools/go/analysis" 12 ) 13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 15 Analyzer: &analysis.Analyzer{ 16 Name: "SA1024", 17 Requires: []*analysis.Analyzer{buildir.Analyzer}, 18 Run: callcheck.Analyzer(rules), 19 }, 20 Doc: &lint.Documentation{ 21 Title: `A string cutset contains duplicate characters`, 22 Text: `The \'strings.TrimLeft\' and \'strings.TrimRight\' functions take cutsets, not 23 prefixes. A cutset is treated as a set of characters to remove from a 24 string. For example, 25 26 strings.TrimLeft("42133word", "1234") 27 28 will result in the string \'"word"\' – any characters that are 1, 2, 3 or 29 4 are cut from the left of the string. 30 31 In order to remove one string from another, use \'strings.TrimPrefix\' instead.`, 32 Since: "2017.1", 33 Severity: lint.SeverityWarning, 34 MergeIf: lint.MergeIfAny, 35 }, 36 }) 37 38 var Analyzer = SCAnalyzer.Analyzer 39 40 var rules = map[string]callcheck.Check{ 41 "strings.Trim": check, 42 "strings.TrimLeft": check, 43 "strings.TrimRight": check, 44 } 45 46 func check(call *callcheck.Call) { 47 arg := call.Args[1] 48 if !isUniqueStringCutset(arg.Value) { 49 const MsgNonUniqueCutset = "cutset contains duplicate characters" 50 arg.Invalid(MsgNonUniqueCutset) 51 } 52 } 53 54 func isUniqueStringCutset(v callcheck.Value) bool { 55 if c := callcheck.ExtractConstExpectKind(v, constant.String); c != nil { 56 s := constant.StringVal(c.Value) 57 rs := runeSlice(s) 58 if len(rs) < 2 { 59 return true 60 } 61 sort.Sort(rs) 62 for i, r := range rs[1:] { 63 if rs[i] == r { 64 return false 65 } 66 } 67 } 68 return true 69 } 70 71 type runeSlice []rune 72 73 func (rs runeSlice) Len() int { return len(rs) } 74 func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] } 75 func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] }