src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1024.go (1946B)


      1 package sa1024
      2 
      3 import (
      4 	"go/constant"
      5 	"sort"
      6 
      7 	"honnef.co/go/tools/analysis/callcheck"
      8 	"honnef.co/go/tools/analysis/lint"
      9 	"honnef.co/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.RawDocumentation{
     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] }