diff --git a/checkers/rules/rules.go b/checkers/rules/rules.go index a612c90b6..ca5a5c067 100644 --- a/checkers/rules/rules.go +++ b/checkers/rules/rules.go @@ -736,3 +736,20 @@ func dynamicFmtString(m dsl.Matcher) { Suggest("errors.New($f($*args))"). Report(`use errors.New($f($*args)) or fmt.Errorf("%s", $f($*args)) instead`) } + +//doc:summary Detects strings.Compare usage +//doc:tags style experimental +//doc:before strings.Compare(x, y) +//doc:after x < y +func stringsCompare(m dsl.Matcher) { + m.Match(`strings.Compare($s1, $s2) == 0`). + Suggest(`$s1 == $s2`) + + m.Match(`strings.Compare($s1, $s2) == -1`, + `strings.Compare($s1, $s2) < 0`). + Suggest(`$s1 < $s2`) + + m.Match(`strings.Compare($s1, $s2) == 1`, + `strings.Compare($s1, $s2) > 0`). + Suggest(`$s1 > $s2`) +} diff --git a/checkers/rulesdata/rulesdata.go b/checkers/rulesdata/rulesdata.go index 8a952b7c2..b759c4abb 100644 --- a/checkers/rulesdata/rulesdata.go +++ b/checkers/rulesdata/rulesdata.go @@ -2745,6 +2745,46 @@ var PrecompiledRules = &ir.File{ }, }, }, + ir.RuleGroup{ + Line: 744, + Name: "stringsCompare", + MatcherName: "m", + DocTags: []string{ + "style", + "experimental", + }, + DocSummary: "Detects strings.Compare usage", + DocBefore: "strings.Compare(x, y)", + DocAfter: "x < y", + Rules: []ir.Rule{ + ir.Rule{ + Line: 745, + SyntaxPatterns: []ir.PatternString{ + ir.PatternString{Line: 745, Value: "strings.Compare($s1, $s2) == 0"}, + }, + ReportTemplate: "suggestion: $s1 == $s2", + SuggestTemplate: "$s1 == $s2", + }, + ir.Rule{ + Line: 748, + SyntaxPatterns: []ir.PatternString{ + ir.PatternString{Line: 748, Value: "strings.Compare($s1, $s2) == -1"}, + ir.PatternString{Line: 749, Value: "strings.Compare($s1, $s2) < 0"}, + }, + ReportTemplate: "suggestion: $s1 < $s2", + SuggestTemplate: "$s1 < $s2", + }, + ir.Rule{ + Line: 752, + SyntaxPatterns: []ir.PatternString{ + ir.PatternString{Line: 752, Value: "strings.Compare($s1, $s2) == 1"}, + ir.PatternString{Line: 753, Value: "strings.Compare($s1, $s2) > 0"}, + }, + ReportTemplate: "suggestion: $s1 > $s2", + SuggestTemplate: "$s1 > $s2", + }, + }, + }, }, } diff --git a/checkers/testdata/stringsCompare/negative_tests.go b/checkers/testdata/stringsCompare/negative_tests.go new file mode 100644 index 000000000..5cc0420d7 --- /dev/null +++ b/checkers/testdata/stringsCompare/negative_tests.go @@ -0,0 +1,78 @@ +package checker_test + +import ( + "bytes" + "strings" +) + +type s []string + +func (s) Compare(x, y string) int { + if x < y { + return 1 + } + return 0 +} + +func negative() { + bytes.Compare([]byte{}, []byte{}) + + strings := s{} + _ = strings.Compare("1", "3") == 0 +} + +func negative2() { + var a, b = "aaa", "bbb" + if a > b { + print(1) + } + + if a == b { + print(0) + } + + if a < b { + print(-1) + } + + switch a > b { + case true: + print(1) + case false: + print(0, -1) + } +} + +func negative3() { + if "aaa" > "bbb" { + print(1) + } + + if "aaa" == "bbb" { + print(0) + } + + if "aaa" < "bbb" { + print(-1) + } + + switch "aaa" > "bbb" { + case true: + print(1) + case false: + print(0, -1) + } +} + +func negative4() { + f, b := "aaa", "bbb" + + _ = strings.Compare(f, b) > -100 + _ = strings.Compare(f, b) < 100 + _ = strings.Compare(f, b) >= -1 + _ = strings.Compare(f, b) <= -1 + _ = strings.Compare(f, b) >= 1 + _ = strings.Compare(f, b) <= 1 + _ = strings.Compare(f, b) >= 0 + _ = strings.Compare(f, b) <= 0 +} diff --git a/checkers/testdata/stringsCompare/positive_tests.go b/checkers/testdata/stringsCompare/positive_tests.go new file mode 100644 index 000000000..4d1cb51c4 --- /dev/null +++ b/checkers/testdata/stringsCompare/positive_tests.go @@ -0,0 +1,36 @@ +package checker_test + +import ( + "strings" +) + +func warning() { + f, b := "aaa", "bbb" + + /*! suggestion: f == b */ + if strings.Compare(f, b) == 0 { + } + + /*! suggestion: f > b */ + switch foo := strings.Compare(f, b) > 0; foo { + case true: + print(0) + case false: + print(1) + } + + /*! suggestion: "s" < "ww" */ + _ = strings.Compare("s", "ww") < 0 + /*! suggestion: "s" == "ww" */ + _ = strings.Compare("s", "ww") == 0 + /*! suggestion: "s" > "ww" */ + _ = strings.Compare("s", "ww") > 0 + + /*! suggestion: "s" > "ww" */ + _ = strings.Compare("s", "ww") > 0 + + /*! suggestion: "s" > "ww" */ + _ = strings.Compare("s", "ww") == 1 + /*! suggestion: "s" < "ww" */ + _ = strings.Compare("s", "ww") == -1 +}