Documentation ¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var PrecompiledRules = &ir.File{ PkgPath: "gorules", CustomDecls: []string{}, BundleImports: []ir.BundleImport{}, RuleGroups: []ir.RuleGroup{ { Line: 11, Name: "redundantSprint", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects redundant fmt.Sprint calls", DocBefore: "fmt.Sprint(x)", DocAfter: "x.String()", Rules: []ir.Rule{ { Line: 12, SyntaxPatterns: []ir.PatternString{ {Line: 12, Value: "fmt.Sprint($x)"}, {Line: 12, Value: "fmt.Sprintf(\"%s\", $x)"}, {Line: 12, Value: "fmt.Sprintf(\"%v\", $x)"}, }, ReportTemplate: "use $x.String() instead", SuggestTemplate: "$x.String()", WhereExpr: ir.FilterExpr{ Line: 13, Op: ir.FilterAndOp, Src: "!m[\"x\"].Type.Is(`reflect.Value`) && m[\"x\"].Type.Implements(`fmt.Stringer`)", Args: []ir.FilterExpr{ { Line: 13, Op: ir.FilterNotOp, Src: "!m[\"x\"].Type.Is(`reflect.Value`)", Args: []ir.FilterExpr{{ Line: 13, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`reflect.Value`)", Value: "x", Args: []ir.FilterExpr{{Line: 13, Op: ir.FilterStringOp, Src: "`reflect.Value`", Value: "reflect.Value"}}, }}, }, { Line: 13, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"x\"].Type.Implements(`fmt.Stringer`)", Value: "x", Args: []ir.FilterExpr{{Line: 13, Op: ir.FilterStringOp, Src: "`fmt.Stringer`", Value: "fmt.Stringer"}}, }, }, }, }, { Line: 17, SyntaxPatterns: []ir.PatternString{ {Line: 17, Value: "fmt.Sprint($x)"}, {Line: 17, Value: "fmt.Sprintf(\"%s\", $x)"}, {Line: 17, Value: "fmt.Sprintf(\"%v\", $x)"}, }, ReportTemplate: "$x is already string", SuggestTemplate: "$x", WhereExpr: ir.FilterExpr{ Line: 18, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`string`)", Value: "x", Args: []ir.FilterExpr{{Line: 18, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, { Line: 27, Name: "deferUnlambda", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects deferred function literals that can be simplified", DocBefore: "defer func() { f() }()", DocAfter: "defer f()", Rules: []ir.Rule{ { Line: 28, SyntaxPatterns: []ir.PatternString{{Line: 28, Value: "defer func() { $f($*args) }()"}}, ReportTemplate: "can rewrite as `defer $f($args)`", WhereExpr: ir.FilterExpr{ Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\" && m[\"f\"].Text != \"recover\" && m[\"args\"].Const", Args: []ir.FilterExpr{ { Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\" && m[\"f\"].Text != \"recover\"", Args: []ir.FilterExpr{ { Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\"", Args: []ir.FilterExpr{ { Line: 29, Op: ir.FilterVarNodeIsOp, Src: "m[\"f\"].Node.Is(`Ident`)", Value: "f", Args: []ir.FilterExpr{{Line: 29, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, }, { Line: 29, Op: ir.FilterNeqOp, Src: "m[\"f\"].Text != \"panic\"", Args: []ir.FilterExpr{ {Line: 29, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, {Line: 29, Op: ir.FilterStringOp, Src: "\"panic\"", Value: "panic"}, }, }, }, }, { Line: 29, Op: ir.FilterNeqOp, Src: "m[\"f\"].Text != \"recover\"", Args: []ir.FilterExpr{ {Line: 29, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, {Line: 29, Op: ir.FilterStringOp, Src: "\"recover\"", Value: "recover"}, }, }, }, }, { Line: 29, Op: ir.FilterVarConstOp, Src: "m[\"args\"].Const", Value: "args", }, }, }, }, { Line: 32, SyntaxPatterns: []ir.PatternString{{Line: 32, Value: "defer func() { $pkg.$f($*args) }()"}}, ReportTemplate: "can rewrite as `defer $pkg.$f($args)`", WhereExpr: ir.FilterExpr{ Line: 33, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"args\"].Const && m[\"pkg\"].Object.Is(`PkgName`)", Args: []ir.FilterExpr{ { Line: 33, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"args\"].Const", Args: []ir.FilterExpr{ { Line: 33, Op: ir.FilterVarNodeIsOp, Src: "m[\"f\"].Node.Is(`Ident`)", Value: "f", Args: []ir.FilterExpr{{Line: 33, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, }, { Line: 33, Op: ir.FilterVarConstOp, Src: "m[\"args\"].Const", Value: "args", }, }, }, { Line: 33, Op: ir.FilterVarObjectIsOp, Src: "m[\"pkg\"].Object.Is(`PkgName`)", Value: "pkg", Args: []ir.FilterExpr{{Line: 33, Op: ir.FilterStringOp, Src: "`PkgName`", Value: "PkgName"}}, }, }, }, }, }, }, { Line: 41, Name: "badLock", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects suspicious mutex lock/unlock operations", DocBefore: "mu.Lock(); mu.Unlock()", DocAfter: "mu.Lock(); defer mu.Unlock()", Rules: []ir.Rule{ { Line: 45, SyntaxPatterns: []ir.PatternString{{Line: 45, Value: "$mu1.Lock(); $mu2.Unlock()"}}, ReportTemplate: "defer is missing, mutex is unlocked immediately", WhereExpr: ir.FilterExpr{ Line: 46, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 46, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 46, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { Line: 50, SyntaxPatterns: []ir.PatternString{{Line: 50, Value: "$mu1.RLock(); $mu2.RUnlock()"}}, ReportTemplate: "defer is missing, mutex is unlocked immediately", WhereExpr: ir.FilterExpr{ Line: 51, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { Line: 56, SyntaxPatterns: []ir.PatternString{{Line: 56, Value: "$mu1.Lock(); defer $mu2.RUnlock()"}}, ReportTemplate: "suspicious unlock, maybe Unlock was intended?", WhereExpr: ir.FilterExpr{ Line: 57, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 57, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 57, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { Line: 61, SyntaxPatterns: []ir.PatternString{{Line: 61, Value: "$mu1.RLock(); defer $mu2.Unlock()"}}, ReportTemplate: "suspicious unlock, maybe RUnlock was intended?", WhereExpr: ir.FilterExpr{ Line: 62, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { Line: 67, SyntaxPatterns: []ir.PatternString{{Line: 67, Value: "$mu1.Lock(); defer $mu2.Lock()"}}, ReportTemplate: "maybe defer $mu1.Unlock() was intended?", WhereExpr: ir.FilterExpr{ Line: 68, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 68, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 68, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { Line: 72, SyntaxPatterns: []ir.PatternString{{Line: 72, Value: "$mu1.RLock(); defer $mu2.RLock()"}}, ReportTemplate: "maybe defer $mu1.RUnlock() was intended?", WhereExpr: ir.FilterExpr{ Line: 73, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, }, }, { Line: 82, Name: "httpNoBody", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects nil usages in http.NewRequest calls, suggesting http.NoBody as an alternative", DocBefore: "http.NewRequest(\"GET\", url, nil)", DocAfter: "http.NewRequest(\"GET\", url, http.NoBody)", Rules: []ir.Rule{ { Line: 83, SyntaxPatterns: []ir.PatternString{{Line: 83, Value: "http.NewRequest($method, $url, $nil)"}}, ReportTemplate: "http.NoBody should be preferred to the nil request body", SuggestTemplate: "http.NewRequest($method, $url, http.NoBody)", WhereExpr: ir.FilterExpr{ Line: 84, Op: ir.FilterEqOp, Src: "m[\"nil\"].Text == \"nil\"", Args: []ir.FilterExpr{ {Line: 84, Op: ir.FilterVarTextOp, Src: "m[\"nil\"].Text", Value: "nil"}, {Line: 84, Op: ir.FilterStringOp, Src: "\"nil\"", Value: "nil"}, }, }, }, { Line: 88, SyntaxPatterns: []ir.PatternString{{Line: 88, Value: "http.NewRequestWithContext($ctx, $method, $url, $nil)"}}, ReportTemplate: "http.NoBody should be preferred to the nil request body", SuggestTemplate: "http.NewRequestWithContext($ctx, $method, $url, http.NoBody)", WhereExpr: ir.FilterExpr{ Line: 89, Op: ir.FilterEqOp, Src: "m[\"nil\"].Text == \"nil\"", Args: []ir.FilterExpr{ {Line: 89, Op: ir.FilterVarTextOp, Src: "m[\"nil\"].Text", Value: "nil"}, {Line: 89, Op: ir.FilterStringOp, Src: "\"nil\"", Value: "nil"}, }, }, }, }, }, { Line: 99, Name: "preferDecodeRune", MatcherName: "m", DocTags: []string{"performance", "experimental"}, DocSummary: "Detects expressions like []rune(s)[0] that may cause unwanted rune slice allocation", DocBefore: "r := []rune(s)[0]", DocAfter: "r, _ := utf8.DecodeRuneInString(s)", DocNote: "See Go issue for details: https://github.com/golang/go/issues/45260", Rules: []ir.Rule{{ Line: 100, SyntaxPatterns: []ir.PatternString{{Line: 100, Value: "[]rune($s)[0]"}}, ReportTemplate: "consider replacing $$ with utf8.DecodeRuneInString($s)", WhereExpr: ir.FilterExpr{ Line: 101, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 101, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }}, }, { Line: 109, Name: "sloppyLen", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects usage of `len` when result is obvious or doesn't make sense", DocBefore: "len(arr) <= 0", DocAfter: "len(arr) == 0", Rules: []ir.Rule{ { Line: 110, SyntaxPatterns: []ir.PatternString{{Line: 110, Value: "len($_) >= 0"}}, ReportTemplate: "$$ is always true", }, { Line: 111, SyntaxPatterns: []ir.PatternString{{Line: 111, Value: "len($_) < 0"}}, ReportTemplate: "$$ is always false", }, { Line: 112, SyntaxPatterns: []ir.PatternString{{Line: 112, Value: "len($x) <= 0"}}, ReportTemplate: "$$ can be len($x) == 0", }, }, }, { Line: 119, Name: "valSwap", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects value swapping code that are not using parallel assignment", DocBefore: "*tmp = *x; *x = *y; *y = *tmp", DocAfter: "*x, *y = *y, *x", Rules: []ir.Rule{{ Line: 120, SyntaxPatterns: []ir.PatternString{{Line: 120, Value: "$tmp := $y; $y = $x; $x = $tmp"}}, ReportTemplate: "can re-write as `$y, $x = $x, $y`", }}, }, { Line: 128, Name: "switchTrue", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects switch-over-bool statements that use explicit `true` tag value", DocBefore: "switch true {...}", DocAfter: "switch {...}", Rules: []ir.Rule{ { Line: 129, SyntaxPatterns: []ir.PatternString{{Line: 129, Value: "switch true { $*_ }"}}, ReportTemplate: "replace 'switch true {}' with 'switch {}'", }, { Line: 131, SyntaxPatterns: []ir.PatternString{{Line: 131, Value: "switch $x; true { $*_ }"}}, ReportTemplate: "replace 'switch $x; true {}' with 'switch $x; {}'", }, }, }, { Line: 139, Name: "flagDeref", MatcherName: "m", DocTags: []string{"diagnostic"}, DocSummary: "Detects immediate dereferencing of `flag` package pointers", DocBefore: "b := *flag.Bool(\"b\", false, \"b docs\")", DocAfter: "var b bool; flag.BoolVar(&b, \"b\", false, \"b docs\")", Rules: []ir.Rule{ { Line: 140, SyntaxPatterns: []ir.PatternString{{Line: 140, Value: "*flag.Bool($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.BoolVar", }, { Line: 141, SyntaxPatterns: []ir.PatternString{{Line: 141, Value: "*flag.Duration($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.DurationVar", }, { Line: 142, SyntaxPatterns: []ir.PatternString{{Line: 142, Value: "*flag.Float64($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.Float64Var", }, { Line: 143, SyntaxPatterns: []ir.PatternString{{Line: 143, Value: "*flag.Int($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.IntVar", }, { Line: 144, SyntaxPatterns: []ir.PatternString{{Line: 144, Value: "*flag.Int64($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.Int64Var", }, { Line: 145, SyntaxPatterns: []ir.PatternString{{Line: 145, Value: "*flag.String($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.StringVar", }, { Line: 146, SyntaxPatterns: []ir.PatternString{{Line: 146, Value: "*flag.Uint($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.UintVar", }, { Line: 147, SyntaxPatterns: []ir.PatternString{{Line: 147, Value: "*flag.Uint64($*_)"}}, ReportTemplate: "immediate deref in $$ is most likely an error; consider using flag.Uint64Var", }, }, }, { Line: 154, Name: "emptyStringTest", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects empty string checks that can be written more idiomatically", DocBefore: "len(s) == 0", DocAfter: "s == \"\"", Rules: []ir.Rule{ { Line: 155, SyntaxPatterns: []ir.PatternString{{Line: 155, Value: "len($s) != 0"}}, ReportTemplate: "replace `$$` with `$s != \"\"`", WhereExpr: ir.FilterExpr{ Line: 156, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 156, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, { Line: 159, SyntaxPatterns: []ir.PatternString{{Line: 159, Value: "len($s) == 0"}}, ReportTemplate: "replace `$$` with `$s == \"\"`", WhereExpr: ir.FilterExpr{ Line: 160, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 160, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, { Line: 168, Name: "stringXbytes", MatcherName: "m", DocTags: []string{"performance"}, DocSummary: "Detects redundant conversions between string and []byte", DocBefore: "copy(b, []byte(s))", DocAfter: "copy(b, s)", Rules: []ir.Rule{ { Line: 169, SyntaxPatterns: []ir.PatternString{{Line: 169, Value: "copy($_, []byte($s))"}}, ReportTemplate: "can simplify `[]byte($s)` to `$s`", }, { Line: 171, SyntaxPatterns: []ir.PatternString{{Line: 171, Value: "string($b) == \"\""}}, ReportTemplate: "suggestion: len($b) == 0", SuggestTemplate: "len($b) == 0", WhereExpr: ir.FilterExpr{ Line: 171, Op: ir.FilterVarTypeIsOp, Src: "m[\"b\"].Type.Is(`[]byte`)", Value: "b", Args: []ir.FilterExpr{{Line: 171, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, }, { Line: 172, SyntaxPatterns: []ir.PatternString{{Line: 172, Value: "string($b) != \"\""}}, ReportTemplate: "suggestion: len($b) != 0", SuggestTemplate: "len($b) != 0", WhereExpr: ir.FilterExpr{ Line: 172, Op: ir.FilterVarTypeIsOp, Src: "m[\"b\"].Type.Is(`[]byte`)", Value: "b", Args: []ir.FilterExpr{{Line: 172, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, }, { Line: 174, SyntaxPatterns: []ir.PatternString{{Line: 174, Value: "len(string($b))"}}, ReportTemplate: "suggestion: len($b)", SuggestTemplate: "len($b)", WhereExpr: ir.FilterExpr{ Line: 174, Op: ir.FilterVarTypeIsOp, Src: "m[\"b\"].Type.Is(`[]byte`)", Value: "b", Args: []ir.FilterExpr{{Line: 174, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, }, { Line: 176, SyntaxPatterns: []ir.PatternString{{Line: 176, Value: "string($x) == string($y)"}}, ReportTemplate: "suggestion: bytes.Equal($x, $y)", SuggestTemplate: "bytes.Equal($x, $y)", WhereExpr: ir.FilterExpr{ Line: 177, Op: ir.FilterAndOp, Src: "m[\"x\"].Type.Is(`[]byte`) && m[\"y\"].Type.Is(`[]byte`)", Args: []ir.FilterExpr{ { Line: 177, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]byte`)", Value: "x", Args: []ir.FilterExpr{{Line: 177, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, { Line: 177, Op: ir.FilterVarTypeIsOp, Src: "m[\"y\"].Type.Is(`[]byte`)", Value: "y", Args: []ir.FilterExpr{{Line: 177, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, }, }, }, { Line: 180, SyntaxPatterns: []ir.PatternString{{Line: 180, Value: "string($x) != string($y)"}}, ReportTemplate: "suggestion: !bytes.Equal($x, $y)", SuggestTemplate: "!bytes.Equal($x, $y)", WhereExpr: ir.FilterExpr{ Line: 181, Op: ir.FilterAndOp, Src: "m[\"x\"].Type.Is(`[]byte`) && m[\"y\"].Type.Is(`[]byte`)", Args: []ir.FilterExpr{ { Line: 181, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]byte`)", Value: "x", Args: []ir.FilterExpr{{Line: 181, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, { Line: 181, Op: ir.FilterVarTypeIsOp, Src: "m[\"y\"].Type.Is(`[]byte`)", Value: "y", Args: []ir.FilterExpr{{Line: 181, Op: ir.FilterStringOp, Src: "`[]byte`", Value: "[]byte"}}, }, }, }, }, { Line: 184, SyntaxPatterns: []ir.PatternString{{Line: 184, Value: "$re.Match([]byte($s))"}}, ReportTemplate: "suggestion: $re.MatchString($s)", SuggestTemplate: "$re.MatchString($s)", WhereExpr: ir.FilterExpr{ Line: 185, Op: ir.FilterAndOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`) && m[\"s\"].Type.Is(`string`)", Args: []ir.FilterExpr{ { Line: 185, Op: ir.FilterVarTypeIsOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`)", Value: "re", Args: []ir.FilterExpr{{Line: 185, Op: ir.FilterStringOp, Src: "`*regexp.Regexp`", Value: "*regexp.Regexp"}}, }, { Line: 185, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 185, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, { Line: 188, SyntaxPatterns: []ir.PatternString{{Line: 188, Value: "$re.FindIndex([]byte($s))"}}, ReportTemplate: "suggestion: $re.FindStringIndex($s)", SuggestTemplate: "$re.FindStringIndex($s)", WhereExpr: ir.FilterExpr{ Line: 189, Op: ir.FilterAndOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`) && m[\"s\"].Type.Is(`string`)", Args: []ir.FilterExpr{ { Line: 189, Op: ir.FilterVarTypeIsOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`)", Value: "re", Args: []ir.FilterExpr{{Line: 189, Op: ir.FilterStringOp, Src: "`*regexp.Regexp`", Value: "*regexp.Regexp"}}, }, { Line: 189, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 189, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, { Line: 192, SyntaxPatterns: []ir.PatternString{{Line: 192, Value: "$re.FindAllIndex([]byte($s), $n)"}}, ReportTemplate: "suggestion: $re.FindAllStringIndex($s, $n)", SuggestTemplate: "$re.FindAllStringIndex($s, $n)", WhereExpr: ir.FilterExpr{ Line: 193, Op: ir.FilterAndOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`) && m[\"s\"].Type.Is(`string`)", Args: []ir.FilterExpr{ { Line: 193, Op: ir.FilterVarTypeIsOp, Src: "m[\"re\"].Type.Is(`*regexp.Regexp`)", Value: "re", Args: []ir.FilterExpr{{Line: 193, Op: ir.FilterStringOp, Src: "`*regexp.Regexp`", Value: "*regexp.Regexp"}}, }, { Line: 193, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 193, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, }, }, { Line: 202, Name: "indexAlloc", MatcherName: "m", DocTags: []string{"performance"}, DocSummary: "Detects strings.Index calls that may cause unwanted allocs", DocBefore: "strings.Index(string(x), y)", DocAfter: "bytes.Index(x, []byte(y))", DocNote: "See Go issue for details: https://github.com/golang/go/issues/25864", Rules: []ir.Rule{{ Line: 203, SyntaxPatterns: []ir.PatternString{{Line: 203, Value: "strings.Index(string($x), $y)"}}, ReportTemplate: "consider replacing $$ with bytes.Index($x, []byte($y))", WhereExpr: ir.FilterExpr{ Line: 204, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure", Args: []ir.FilterExpr{ {Line: 204, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, {Line: 204, Op: ir.FilterVarPureOp, Src: "m[\"y\"].Pure", Value: "y"}, }, }, }}, }, { Line: 212, Name: "wrapperFunc", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects function calls that can be replaced with convenience wrappers", DocBefore: "wg.Add(-1)", DocAfter: "wg.Done()", Rules: []ir.Rule{ { Line: 213, SyntaxPatterns: []ir.PatternString{{Line: 213, Value: "$wg.Add(-1)"}}, ReportTemplate: "use WaitGroup.Done method in `$$`", WhereExpr: ir.FilterExpr{ Line: 214, Op: ir.FilterVarTypeIsOp, Src: "m[\"wg\"].Type.Is(`sync.WaitGroup`)", Value: "wg", Args: []ir.FilterExpr{{Line: 214, Op: ir.FilterStringOp, Src: "`sync.WaitGroup`", Value: "sync.WaitGroup"}}, }, }, { Line: 217, SyntaxPatterns: []ir.PatternString{{Line: 217, Value: "$buf.Truncate(0)"}}, ReportTemplate: "use Buffer.Reset method in `$$`", WhereExpr: ir.FilterExpr{ Line: 218, Op: ir.FilterVarTypeIsOp, Src: "m[\"buf\"].Type.Is(`bytes.Buffer`)", Value: "buf", Args: []ir.FilterExpr{{Line: 218, Op: ir.FilterStringOp, Src: "`bytes.Buffer`", Value: "bytes.Buffer"}}, }, }, { Line: 221, SyntaxPatterns: []ir.PatternString{{Line: 221, Value: "http.HandlerFunc(http.NotFound)"}}, ReportTemplate: "use http.NotFoundHandler method in `$$`", }, { Line: 223, SyntaxPatterns: []ir.PatternString{{Line: 223, Value: "strings.SplitN($_, $_, -1)"}}, ReportTemplate: "use strings.Split method in `$$`", }, { Line: 224, SyntaxPatterns: []ir.PatternString{{Line: 224, Value: "strings.Replace($_, $_, $_, -1)"}}, ReportTemplate: "use strings.ReplaceAll method in `$$`", }, { Line: 225, SyntaxPatterns: []ir.PatternString{{Line: 225, Value: "strings.Map(unicode.ToTitle, $_)"}}, ReportTemplate: "use strings.ToTitle method in `$$`", }, { Line: 226, SyntaxPatterns: []ir.PatternString{ {Line: 226, Value: "strings.Index($s1, $s2) >= 0"}, {Line: 226, Value: "strings.Index($s1, $s2) != -1"}, }, ReportTemplate: "suggestion: strings.Contains($s1, $s2)", SuggestTemplate: "strings.Contains($s1, $s2)", }, { Line: 227, SyntaxPatterns: []ir.PatternString{ {Line: 227, Value: "strings.IndexAny($s1, $s2) >= 0"}, {Line: 227, Value: "strings.IndexAny($s1, $s2) != -1"}, }, ReportTemplate: "suggestion: strings.ContainsAny($s1, $s2)", SuggestTemplate: "strings.ContainsAny($s1, $s2)", }, { Line: 228, SyntaxPatterns: []ir.PatternString{ {Line: 228, Value: "strings.IndexRune($s1, $s2) >= 0"}, {Line: 228, Value: "strings.IndexRune($s1, $s2) != -1"}, }, ReportTemplate: "suggestion: strings.ContainsRune($s1, $s2)", SuggestTemplate: "strings.ContainsRune($s1, $s2)", }, { Line: 230, SyntaxPatterns: []ir.PatternString{ {Line: 230, Value: "$i := strings.Index($s, $sep); $*_; $x, $y = $s[:$i], $s[$i+1:]"}, {Line: 231, Value: "$i := strings.Index($s, $sep); $*_; $x = $s[:$i]; $*_; $y = $s[$i+1:]"}, }, ReportTemplate: "suggestion: $x, $y, _ = strings.Cut($s, $sep)", SuggestTemplate: "$x, $y, _ = strings.Cut($s, $sep)", WhereExpr: ir.FilterExpr{ Line: 232, Op: ir.FilterGoVersionGreaterEqThanOp, Src: "m.GoVersion().GreaterEqThan(\"1.18\")", Value: "1.18", }, }, { Line: 235, SyntaxPatterns: []ir.PatternString{ {Line: 236, Value: "if $i := strings.Index($s, $sep); $i != -1 { $*_; $x, $y = $s[:$i], $s[$i+1:]; $*_ }"}, {Line: 237, Value: "if $i := strings.Index($s, $sep); $i != -1 { $*_; $x = $s[:$i]; $*_; $y = $s[$i+1:]; $*_ }"}, {Line: 238, Value: "if $i := strings.Index($s, $sep); $i >= 0 { $*_; $x, $y = $s[:$i], $s[$i+1:]; $*_ }"}, {Line: 239, Value: "if $i := strings.Index($s, $sep); $i >= 0 { $*_; $x = $s[:$i]; $*_; $y = $s[$i+1:]; $*_ }"}, }, ReportTemplate: "suggestion: if $x, $y, ok = strings.Cut($s, $sep); ok { ... }", SuggestTemplate: "if $x, $y, ok = strings.Cut($s, $sep); ok { ... }", WhereExpr: ir.FilterExpr{ Line: 240, Op: ir.FilterGoVersionGreaterEqThanOp, Src: "m.GoVersion().GreaterEqThan(\"1.18\")", Value: "1.18", }, }, { Line: 243, SyntaxPatterns: []ir.PatternString{{Line: 243, Value: "bytes.SplitN(b, []byte(\".\"), -1)"}}, ReportTemplate: "use bytes.Split method in `$$`", }, { Line: 244, SyntaxPatterns: []ir.PatternString{{Line: 244, Value: "bytes.Replace($_, $_, $_, -1)"}}, ReportTemplate: "use bytes.ReplaceAll method in `$$`", }, { Line: 245, SyntaxPatterns: []ir.PatternString{{Line: 245, Value: "bytes.Map(unicode.ToUpper, $_)"}}, ReportTemplate: "use bytes.ToUpper method in `$$`", }, { Line: 246, SyntaxPatterns: []ir.PatternString{{Line: 246, Value: "bytes.Map(unicode.ToLower, $_)"}}, ReportTemplate: "use bytes.ToLower method in `$$`", }, { Line: 247, SyntaxPatterns: []ir.PatternString{{Line: 247, Value: "bytes.Map(unicode.ToTitle, $_)"}}, ReportTemplate: "use bytes.ToTitle method in `$$`", }, { Line: 248, SyntaxPatterns: []ir.PatternString{ {Line: 248, Value: "bytes.Index($b1, $b2) >= 0"}, {Line: 248, Value: "bytes.Index($b1, $b2) != -1"}, }, ReportTemplate: "suggestion: bytes.Contains($b1, $b2)", SuggestTemplate: "bytes.Contains($b1, $b2)", }, { Line: 249, SyntaxPatterns: []ir.PatternString{ {Line: 249, Value: "bytes.IndexAny($b1, $b2) >= 0"}, {Line: 249, Value: "bytes.IndexAny($b1, $b2) != -1"}, }, ReportTemplate: "suggestion: bytes.ContainsAny($b1, $b2)", SuggestTemplate: "bytes.ContainsAny($b1, $b2)", }, { Line: 250, SyntaxPatterns: []ir.PatternString{ {Line: 250, Value: "bytes.IndexRune($b1, $b2) >= 0"}, {Line: 250, Value: "bytes.IndexRune($b1, $b2) != -1"}, }, ReportTemplate: "suggestion: bytes.ContainsRune($b1, $b2)", SuggestTemplate: "bytes.ContainsRune($b1, $b2)", }, { Line: 252, SyntaxPatterns: []ir.PatternString{{Line: 252, Value: "draw.DrawMask($_, $_, $_, $_, nil, image.Point{}, $_)"}}, ReportTemplate: "use draw.Draw method in `$$`", }, }, }, { Line: 260, Name: "regexpMust", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects `regexp.Compile*` that can be replaced with `regexp.MustCompile*`", DocBefore: "re, _ := regexp.Compile(\"const pattern\")", DocAfter: "re := regexp.MustCompile(\"const pattern\")", Rules: []ir.Rule{ { Line: 261, SyntaxPatterns: []ir.PatternString{{Line: 261, Value: "regexp.Compile($pat)"}}, ReportTemplate: "for const patterns like $pat, use regexp.MustCompile", WhereExpr: ir.FilterExpr{ Line: 262, Op: ir.FilterVarConstOp, Src: "m[\"pat\"].Const", Value: "pat", }, }, { Line: 265, SyntaxPatterns: []ir.PatternString{{Line: 265, Value: "regexp.CompilePOSIX($pat)"}}, ReportTemplate: "for const patterns like $pat, use regexp.MustCompilePOSIX", WhereExpr: ir.FilterExpr{ Line: 266, Op: ir.FilterVarConstOp, Src: "m[\"pat\"].Const", Value: "pat", }, }, }, }, { Line: 274, Name: "badCall", MatcherName: "m", DocTags: []string{"diagnostic"}, DocSummary: "Detects suspicious function calls", DocBefore: "strings.Replace(s, from, to, 0)", DocAfter: "strings.Replace(s, from, to, -1)", Rules: []ir.Rule{ { Line: 275, SyntaxPatterns: []ir.PatternString{{Line: 275, Value: "strings.Replace($_, $_, $_, $zero)"}}, ReportTemplate: "suspicious arg 0, probably meant -1", WhereExpr: ir.FilterExpr{ Line: 276, Op: ir.FilterEqOp, Src: "m[\"zero\"].Value.Int() == 0", Args: []ir.FilterExpr{ { Line: 276, Op: ir.FilterVarValueIntOp, Src: "m[\"zero\"].Value.Int()", Value: "zero", }, { Line: 276, Op: ir.FilterIntOp, Src: "0", Value: int64(0), }, }, }, LocationVar: "zero", }, { Line: 278, SyntaxPatterns: []ir.PatternString{{Line: 278, Value: "bytes.Replace($_, $_, $_, $zero)"}}, ReportTemplate: "suspicious arg 0, probably meant -1", WhereExpr: ir.FilterExpr{ Line: 279, Op: ir.FilterEqOp, Src: "m[\"zero\"].Value.Int() == 0", Args: []ir.FilterExpr{ { Line: 279, Op: ir.FilterVarValueIntOp, Src: "m[\"zero\"].Value.Int()", Value: "zero", }, { Line: 279, Op: ir.FilterIntOp, Src: "0", Value: int64(0), }, }, }, LocationVar: "zero", }, { Line: 282, SyntaxPatterns: []ir.PatternString{{Line: 282, Value: "strings.SplitN($_, $_, $zero)"}}, ReportTemplate: "suspicious arg 0, probably meant -1", WhereExpr: ir.FilterExpr{ Line: 283, Op: ir.FilterEqOp, Src: "m[\"zero\"].Value.Int() == 0", Args: []ir.FilterExpr{ { Line: 283, Op: ir.FilterVarValueIntOp, Src: "m[\"zero\"].Value.Int()", Value: "zero", }, { Line: 283, Op: ir.FilterIntOp, Src: "0", Value: int64(0), }, }, }, LocationVar: "zero", }, { Line: 285, SyntaxPatterns: []ir.PatternString{{Line: 285, Value: "bytes.SplitN($_, $_, $zero)"}}, ReportTemplate: "suspicious arg 0, probably meant -1", WhereExpr: ir.FilterExpr{ Line: 286, Op: ir.FilterEqOp, Src: "m[\"zero\"].Value.Int() == 0", Args: []ir.FilterExpr{ { Line: 286, Op: ir.FilterVarValueIntOp, Src: "m[\"zero\"].Value.Int()", Value: "zero", }, { Line: 286, Op: ir.FilterIntOp, Src: "0", Value: int64(0), }, }, }, LocationVar: "zero", }, { Line: 289, SyntaxPatterns: []ir.PatternString{{Line: 289, Value: "append($_)"}}, ReportTemplate: "no-op append call, probably missing arguments", }, { Line: 291, SyntaxPatterns: []ir.PatternString{{Line: 291, Value: "filepath.Join($_)"}}, ReportTemplate: "suspicious Join on 1 argument", }, }, }, { Line: 298, Name: "assignOp", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects assignments that can be simplified by using assignment operators", DocBefore: "x = x * 2", DocAfter: "x *= 2", Rules: []ir.Rule{ { Line: 299, SyntaxPatterns: []ir.PatternString{{Line: 299, Value: "$x = $x + 1"}}, ReportTemplate: "replace `$$` with `$x++`", WhereExpr: ir.FilterExpr{Line: 299, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 300, SyntaxPatterns: []ir.PatternString{{Line: 300, Value: "$x = $x - 1"}}, ReportTemplate: "replace `$$` with `$x--`", WhereExpr: ir.FilterExpr{Line: 300, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 302, SyntaxPatterns: []ir.PatternString{{Line: 302, Value: "$x = $x + $y"}}, ReportTemplate: "replace `$$` with `$x += $y`", WhereExpr: ir.FilterExpr{Line: 302, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 303, SyntaxPatterns: []ir.PatternString{{Line: 303, Value: "$x = $x - $y"}}, ReportTemplate: "replace `$$` with `$x -= $y`", WhereExpr: ir.FilterExpr{Line: 303, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 305, SyntaxPatterns: []ir.PatternString{{Line: 305, Value: "$x = $x * $y"}}, ReportTemplate: "replace `$$` with `$x *= $y`", WhereExpr: ir.FilterExpr{Line: 305, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 306, SyntaxPatterns: []ir.PatternString{{Line: 306, Value: "$x = $x / $y"}}, ReportTemplate: "replace `$$` with `$x /= $y`", WhereExpr: ir.FilterExpr{Line: 306, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 307, SyntaxPatterns: []ir.PatternString{{Line: 307, Value: "$x = $x % $y"}}, ReportTemplate: "replace `$$` with `$x %= $y`", WhereExpr: ir.FilterExpr{Line: 307, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 308, SyntaxPatterns: []ir.PatternString{{Line: 308, Value: "$x = $x & $y"}}, ReportTemplate: "replace `$$` with `$x &= $y`", WhereExpr: ir.FilterExpr{Line: 308, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 309, SyntaxPatterns: []ir.PatternString{{Line: 309, Value: "$x = $x | $y"}}, ReportTemplate: "replace `$$` with `$x |= $y`", WhereExpr: ir.FilterExpr{Line: 309, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 310, SyntaxPatterns: []ir.PatternString{{Line: 310, Value: "$x = $x ^ $y"}}, ReportTemplate: "replace `$$` with `$x ^= $y`", WhereExpr: ir.FilterExpr{Line: 310, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 311, SyntaxPatterns: []ir.PatternString{{Line: 311, Value: "$x = $x << $y"}}, ReportTemplate: "replace `$$` with `$x <<= $y`", WhereExpr: ir.FilterExpr{Line: 311, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 312, SyntaxPatterns: []ir.PatternString{{Line: 312, Value: "$x = $x >> $y"}}, ReportTemplate: "replace `$$` with `$x >>= $y`", WhereExpr: ir.FilterExpr{Line: 312, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 313, SyntaxPatterns: []ir.PatternString{{Line: 313, Value: "$x = $x &^ $y"}}, ReportTemplate: "replace `$$` with `$x &^= $y`", WhereExpr: ir.FilterExpr{Line: 313, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, }, }, { Line: 320, Name: "preferWriteByte", MatcherName: "m", DocTags: []string{"performance", "experimental", "opinionated"}, DocSummary: "Detects WriteRune calls with rune literal argument that is single byte and reports to use WriteByte instead", DocBefore: "w.WriteRune('\\n')", DocAfter: "w.WriteByte('\\n')", Rules: []ir.Rule{{ Line: 324, SyntaxPatterns: []ir.PatternString{{Line: 324, Value: "$w.WriteRune($c)"}}, ReportTemplate: "consider writing single byte rune $c with $w.WriteByte($c)", WhereExpr: ir.FilterExpr{ Line: 325, Op: ir.FilterAndOp, Src: "m[\"w\"].Type.Implements(\"io.ByteWriter\") && (m[\"c\"].Const && m[\"c\"].Value.Int() < runeSelf)", Args: []ir.FilterExpr{ { Line: 325, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.ByteWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 325, Op: ir.FilterStringOp, Src: "\"io.ByteWriter\"", Value: "io.ByteWriter"}}, }, { Line: 325, Op: ir.FilterAndOp, Src: "(m[\"c\"].Const && m[\"c\"].Value.Int() < runeSelf)", Args: []ir.FilterExpr{ { Line: 325, Op: ir.FilterVarConstOp, Src: "m[\"c\"].Const", Value: "c", }, { Line: 325, Op: ir.FilterLtOp, Src: "m[\"c\"].Value.Int() < runeSelf", Args: []ir.FilterExpr{ { Line: 325, Op: ir.FilterVarValueIntOp, Src: "m[\"c\"].Value.Int()", Value: "c", }, { Line: 325, Op: ir.FilterIntOp, Src: "runeSelf", Value: int64(128), }, }, }, }, }, }, }, }}, }, { Line: 333, Name: "preferFprint", MatcherName: "m", DocTags: []string{"performance", "experimental"}, DocSummary: "Detects fmt.Sprint(f/ln) calls which can be replaced with fmt.Fprint(f/ln)", DocBefore: "w.Write([]byte(fmt.Sprintf(\"%x\", 10)))", DocAfter: "fmt.Fprintf(w, \"%x\", 10)", Rules: []ir.Rule{ { Line: 334, SyntaxPatterns: []ir.PatternString{{Line: 334, Value: "$w.Write([]byte(fmt.Sprint($*args)))"}}, ReportTemplate: "fmt.Fprint($w, $args) should be preferred to the $$", SuggestTemplate: "fmt.Fprint($w, $args)", WhereExpr: ir.FilterExpr{ Line: 335, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 335, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, }, { Line: 339, SyntaxPatterns: []ir.PatternString{{Line: 339, Value: "$w.Write([]byte(fmt.Sprintf($*args)))"}}, ReportTemplate: "fmt.Fprintf($w, $args) should be preferred to the $$", SuggestTemplate: "fmt.Fprintf($w, $args)", WhereExpr: ir.FilterExpr{ Line: 340, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 340, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, }, { Line: 344, SyntaxPatterns: []ir.PatternString{{Line: 344, Value: "$w.Write([]byte(fmt.Sprintln($*args)))"}}, ReportTemplate: "fmt.Fprintln($w, $args) should be preferred to the $$", SuggestTemplate: "fmt.Fprintln($w, $args)", WhereExpr: ir.FilterExpr{ Line: 345, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 345, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, }, { Line: 349, SyntaxPatterns: []ir.PatternString{{Line: 349, Value: "io.WriteString($w, fmt.Sprint($*args))"}}, ReportTemplate: "suggestion: fmt.Fprint($w, $args)", SuggestTemplate: "fmt.Fprint($w, $args)", }, { Line: 350, SyntaxPatterns: []ir.PatternString{{Line: 350, Value: "io.WriteString($w, fmt.Sprintf($*args))"}}, ReportTemplate: "suggestion: fmt.Fprintf($w, $args)", SuggestTemplate: "fmt.Fprintf($w, $args)", }, { Line: 351, SyntaxPatterns: []ir.PatternString{{Line: 351, Value: "io.WriteString($w, fmt.Sprintln($*args))"}}, ReportTemplate: "suggestion: fmt.Fprintln($w, $args)", SuggestTemplate: "fmt.Fprintln($w, $args)", }, { Line: 353, SyntaxPatterns: []ir.PatternString{{Line: 353, Value: "$w.WriteString(fmt.Sprint($*args))"}}, ReportTemplate: "suggestion: fmt.Fprint($w, $args)", SuggestTemplate: "fmt.Fprint($w, $args)", WhereExpr: ir.FilterExpr{ Line: 354, Op: ir.FilterAndOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\") && m[\"w\"].Type.Implements(\"io.StringWriter\")", Args: []ir.FilterExpr{ { Line: 354, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 354, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, { Line: 354, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.StringWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 354, Op: ir.FilterStringOp, Src: "\"io.StringWriter\"", Value: "io.StringWriter"}}, }, }, }, }, { Line: 356, SyntaxPatterns: []ir.PatternString{{Line: 356, Value: "$w.WriteString(fmt.Sprintf($*args))"}}, ReportTemplate: "suggestion: fmt.Fprintf($w, $args)", SuggestTemplate: "fmt.Fprintf($w, $args)", WhereExpr: ir.FilterExpr{ Line: 357, Op: ir.FilterAndOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\") && m[\"w\"].Type.Implements(\"io.StringWriter\")", Args: []ir.FilterExpr{ { Line: 357, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 357, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, { Line: 357, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.StringWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 357, Op: ir.FilterStringOp, Src: "\"io.StringWriter\"", Value: "io.StringWriter"}}, }, }, }, }, { Line: 359, SyntaxPatterns: []ir.PatternString{{Line: 359, Value: "$w.WriteString(fmt.Sprintln($*args))"}}, ReportTemplate: "suggestion: fmt.Fprintln($w, $args)", SuggestTemplate: "fmt.Fprintln($w, $args)", WhereExpr: ir.FilterExpr{ Line: 360, Op: ir.FilterAndOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\") && m[\"w\"].Type.Implements(\"io.StringWriter\")", Args: []ir.FilterExpr{ { Line: 360, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.Writer\")", Value: "w", Args: []ir.FilterExpr{{Line: 360, Op: ir.FilterStringOp, Src: "\"io.Writer\"", Value: "io.Writer"}}, }, { Line: 360, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.StringWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 360, Op: ir.FilterStringOp, Src: "\"io.StringWriter\"", Value: "io.StringWriter"}}, }, }, }, }, }, }, { Line: 368, Name: "dupArg", MatcherName: "m", DocTags: []string{"diagnostic"}, DocSummary: "Detects suspicious duplicated arguments", DocBefore: "copy(dst, dst)", DocAfter: "copy(dst, src)", Rules: []ir.Rule{ { Line: 369, SyntaxPatterns: []ir.PatternString{ {Line: 369, Value: "$x.Equal($x)"}, {Line: 369, Value: "$x.Equals($x)"}, {Line: 369, Value: "$x.Compare($x)"}, {Line: 369, Value: "$x.Cmp($x)"}, }, ReportTemplate: "suspicious method call with the same argument and receiver", WhereExpr: ir.FilterExpr{Line: 370, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, { Line: 373, SyntaxPatterns: []ir.PatternString{ {Line: 373, Value: "copy($x, $x)"}, {Line: 374, Value: "math.Max($x, $x)"}, {Line: 375, Value: "math.Min($x, $x)"}, {Line: 376, Value: "reflect.Copy($x, $x)"}, {Line: 377, Value: "reflect.DeepEqual($x, $x)"}, {Line: 378, Value: "strings.Contains($x, $x)"}, {Line: 379, Value: "strings.Compare($x, $x)"}, {Line: 380, Value: "strings.EqualFold($x, $x)"}, {Line: 381, Value: "strings.HasPrefix($x, $x)"}, {Line: 382, Value: "strings.HasSuffix($x, $x)"}, {Line: 383, Value: "strings.Index($x, $x)"}, {Line: 384, Value: "strings.LastIndex($x, $x)"}, {Line: 385, Value: "strings.Split($x, $x)"}, {Line: 386, Value: "strings.SplitAfter($x, $x)"}, {Line: 387, Value: "strings.SplitAfterN($x, $x, $_)"}, {Line: 388, Value: "strings.SplitN($x, $x, $_)"}, {Line: 389, Value: "strings.Replace($_, $x, $x, $_)"}, {Line: 390, Value: "strings.ReplaceAll($_, $x, $x)"}, {Line: 391, Value: "bytes.Contains($x, $x)"}, {Line: 392, Value: "bytes.Compare($x, $x)"}, {Line: 393, Value: "bytes.Equal($x, $x)"}, {Line: 394, Value: "bytes.EqualFold($x, $x)"}, {Line: 395, Value: "bytes.HasPrefix($x, $x)"}, {Line: 396, Value: "bytes.HasSuffix($x, $x)"}, {Line: 397, Value: "bytes.Index($x, $x)"}, {Line: 398, Value: "bytes.LastIndex($x, $x)"}, {Line: 399, Value: "bytes.Split($x, $x)"}, {Line: 400, Value: "bytes.SplitAfter($x, $x)"}, {Line: 401, Value: "bytes.SplitAfterN($x, $x, $_)"}, {Line: 402, Value: "bytes.SplitN($x, $x, $_)"}, {Line: 403, Value: "bytes.Replace($_, $x, $x, $_)"}, {Line: 404, Value: "bytes.ReplaceAll($_, $x, $x)"}, {Line: 405, Value: "types.Identical($x, $x)"}, {Line: 406, Value: "types.IdenticalIgnoreTags($x, $x)"}, {Line: 407, Value: "draw.Draw($x, $_, $x, $_, $_)"}, }, ReportTemplate: "suspicious duplicated args in $$", WhereExpr: ir.FilterExpr{Line: 408, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, }, }, }, { Line: 416, Name: "returnAfterHttpError", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects suspicious http.Error call without following return", DocBefore: "if err != nil { http.Error(...); }", DocAfter: "if err != nil { http.Error(...); return; }", Rules: []ir.Rule{{ Line: 417, SyntaxPatterns: []ir.PatternString{{Line: 417, Value: "if $_ { $*_; http.Error($w, $err, $code) }"}}, ReportTemplate: "Possibly return is missed after the http.Error call", LocationVar: "w", }}, }, { Line: 426, Name: "preferFilepathJoin", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects concatenation with os.PathSeparator which can be replaced with filepath.Join", DocBefore: "x + string(os.PathSeparator) + y", DocAfter: "filepath.Join(x, y)", Rules: []ir.Rule{{ Line: 427, SyntaxPatterns: []ir.PatternString{{Line: 427, Value: "$x + string(os.PathSeparator) + $y"}}, ReportTemplate: "filepath.Join($x, $y) should be preferred to the $$", SuggestTemplate: "filepath.Join($x, $y)", WhereExpr: ir.FilterExpr{ Line: 428, Op: ir.FilterAndOp, Src: "m[\"x\"].Type.Is(`string`) && m[\"y\"].Type.Is(`string`)", Args: []ir.FilterExpr{ { Line: 428, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`string`)", Value: "x", Args: []ir.FilterExpr{{Line: 428, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, { Line: 428, Op: ir.FilterVarTypeIsOp, Src: "m[\"y\"].Type.Is(`string`)", Value: "y", Args: []ir.FilterExpr{{Line: 428, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }}, }, { Line: 437, Name: "preferStringWriter", MatcherName: "m", DocTags: []string{"performance", "experimental"}, DocSummary: "Detects w.Write or io.WriteString calls which can be replaced with w.WriteString", DocBefore: "w.Write([]byte(\"foo\"))", DocAfter: "w.WriteString(\"foo\")", Rules: []ir.Rule{ { Line: 438, SyntaxPatterns: []ir.PatternString{{Line: 438, Value: "$w.Write([]byte($s))"}}, ReportTemplate: "$w.WriteString($s) should be preferred to the $$", SuggestTemplate: "$w.WriteString($s)", WhereExpr: ir.FilterExpr{ Line: 439, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.StringWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 439, Op: ir.FilterStringOp, Src: "\"io.StringWriter\"", Value: "io.StringWriter"}}, }, }, { Line: 443, SyntaxPatterns: []ir.PatternString{{Line: 443, Value: "io.WriteString($w, $s)"}}, ReportTemplate: "$w.WriteString($s) should be preferred to the $$", SuggestTemplate: "$w.WriteString($s)", WhereExpr: ir.FilterExpr{ Line: 444, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"w\"].Type.Implements(\"io.StringWriter\")", Value: "w", Args: []ir.FilterExpr{{Line: 444, Op: ir.FilterStringOp, Src: "\"io.StringWriter\"", Value: "io.StringWriter"}}, }, }, }, }, { Line: 453, Name: "sliceClear", MatcherName: "m", DocTags: []string{"performance", "experimental"}, DocSummary: "Detects slice clear loops, suggests an idiom that is recognized by the Go compiler", DocBefore: "for i := 0; i < len(buf); i++ { buf[i] = 0 }", DocAfter: "for i := range buf { buf[i] = 0 }", Rules: []ir.Rule{{ Line: 454, SyntaxPatterns: []ir.PatternString{{Line: 454, Value: "for $i := 0; $i < len($xs); $i++ { $xs[$i] = $zero }"}}, ReportTemplate: "rewrite as for-range so compiler can recognize this pattern", WhereExpr: ir.FilterExpr{ Line: 455, Op: ir.FilterEqOp, Src: "m[\"zero\"].Value.Int() == 0", Args: []ir.FilterExpr{ { Line: 455, Op: ir.FilterVarValueIntOp, Src: "m[\"zero\"].Value.Int()", Value: "zero", }, { Line: 455, Op: ir.FilterIntOp, Src: "0", Value: int64(0), }, }, }, }}, }, { Line: 463, Name: "syncMapLoadAndDelete", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects sync.Map load+delete operations that can be replaced with LoadAndDelete", DocBefore: "v, ok := m.Load(k); if ok { m.Delete($k); f(v); }", DocAfter: "v, deleted := m.LoadAndDelete(k); if deleted { f(v) }", Rules: []ir.Rule{{ Line: 464, SyntaxPatterns: []ir.PatternString{{Line: 464, Value: "$_, $ok := $m.Load($k); if $ok { $m.Delete($k); $*_ }"}}, ReportTemplate: "use $m.LoadAndDelete to perform load+delete operations atomically", WhereExpr: ir.FilterExpr{ Line: 465, Op: ir.FilterAndOp, Src: "m.GoVersion().GreaterEqThan(\"1.15\") &&\n\tm[\"m\"].Type.Is(`*sync.Map`)", Args: []ir.FilterExpr{ { Line: 465, Op: ir.FilterGoVersionGreaterEqThanOp, Src: "m.GoVersion().GreaterEqThan(\"1.15\")", Value: "1.15", }, { Line: 466, Op: ir.FilterVarTypeIsOp, Src: "m[\"m\"].Type.Is(`*sync.Map`)", Value: "m", Args: []ir.FilterExpr{{Line: 466, Op: ir.FilterStringOp, Src: "`*sync.Map`", Value: "*sync.Map"}}, }, }, }, }}, }, { Line: 474, Name: "sprintfQuotedString", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects \"%s\" formatting directives that can be replaced with %q", DocBefore: "fmt.Sprintf(`\"%s\"`, s)", DocAfter: "fmt.Sprintf(`%q`, s)", Rules: []ir.Rule{{ Line: 475, SyntaxPatterns: []ir.PatternString{{Line: 475, Value: "fmt.Sprintf($s, $*_)"}}, ReportTemplate: "use %q instead of \"%s\" for quoted strings", WhereExpr: ir.FilterExpr{ Line: 476, Op: ir.FilterOrOp, Src: "m[\"s\"].Text.Matches(\"^`.*\\\"%s\\\".*`$\") ||\n\tm[\"s\"].Text.Matches(`^\".*\\\\\"%s\\\\\".*\"$`)", Args: []ir.FilterExpr{ { Line: 476, Op: ir.FilterVarTextMatchesOp, Src: "m[\"s\"].Text.Matches(\"^`.*\\\"%s\\\".*`$\")", Value: "s", Args: []ir.FilterExpr{{Line: 476, Op: ir.FilterStringOp, Src: "\"^`.*\\\"%s\\\".*`$\"", Value: "^`.*\"%s\".*`$"}}, }, { Line: 477, Op: ir.FilterVarTextMatchesOp, Src: "m[\"s\"].Text.Matches(`^\".*\\\\\"%s\\\\\".*\"$`)", Value: "s", Args: []ir.FilterExpr{{Line: 477, Op: ir.FilterStringOp, Src: "`^\".*\\\\\"%s\\\\\".*\"$`", Value: "^\".*\\\\\"%s\\\\\".*\"$"}}, }, }, }, }}, }, { Line: 485, Name: "offBy1", MatcherName: "m", DocTags: []string{"diagnostic"}, DocSummary: "Detects various off-by-one kind of errors", DocBefore: "xs[len(xs)]", DocAfter: "xs[len(xs)-1]", Rules: []ir.Rule{ { Line: 486, SyntaxPatterns: []ir.PatternString{{Line: 486, Value: "$x[len($x)]"}}, ReportTemplate: "index expr always panics; maybe you wanted $x[len($x)-1]?", SuggestTemplate: "$x[len($x)-1]", WhereExpr: ir.FilterExpr{ Line: 487, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"x\"].Type.Is(`[]$_`)", Args: []ir.FilterExpr{ {Line: 487, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, { Line: 487, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]$_`)", Value: "x", Args: []ir.FilterExpr{{Line: 487, Op: ir.FilterStringOp, Src: "`[]$_`", Value: "[]$_"}}, }, }, }, }, { Line: 494, SyntaxPatterns: []ir.PatternString{ {Line: 495, Value: "$i := strings.Index($s, $_); $_ := $slicing[$i:]"}, {Line: 496, Value: "$i := strings.Index($s, $_); $_ = $slicing[$i:]"}, {Line: 497, Value: "$i := bytes.Index($s, $_); $_ := $slicing[$i:]"}, {Line: 498, Value: "$i := bytes.Index($s, $_); $_ = $slicing[$i:]"}, }, ReportTemplate: "Index() can return -1; maybe you wanted to do $s[$i+1:]", WhereExpr: ir.FilterExpr{ Line: 499, Op: ir.FilterEqOp, Src: "m[\"s\"].Text == m[\"slicing\"].Text", Args: []ir.FilterExpr{ {Line: 499, Op: ir.FilterVarTextOp, Src: "m[\"s\"].Text", Value: "s"}, {Line: 499, Op: ir.FilterVarTextOp, Src: "m[\"slicing\"].Text", Value: "slicing"}, }, }, LocationVar: "slicing", }, { Line: 503, SyntaxPatterns: []ir.PatternString{ {Line: 504, Value: "$i := strings.Index($s, $_); $_ := $slicing[:$i]"}, {Line: 505, Value: "$i := strings.Index($s, $_); $_ = $slicing[:$i]"}, {Line: 506, Value: "$i := bytes.Index($s, $_); $_ := $slicing[:$i]"}, {Line: 507, Value: "$i := bytes.Index($s, $_); $_ = $slicing[:$i]"}, }, ReportTemplate: "Index() can return -1; maybe you wanted to do $s[:$i+1]", WhereExpr: ir.FilterExpr{ Line: 508, Op: ir.FilterEqOp, Src: "m[\"s\"].Text == m[\"slicing\"].Text", Args: []ir.FilterExpr{ {Line: 508, Op: ir.FilterVarTextOp, Src: "m[\"s\"].Text", Value: "s"}, {Line: 508, Op: ir.FilterVarTextOp, Src: "m[\"slicing\"].Text", Value: "slicing"}, }, }, LocationVar: "slicing", }, { Line: 512, SyntaxPatterns: []ir.PatternString{ {Line: 513, Value: "$s[strings.Index($s, $_):]"}, {Line: 514, Value: "$s[:strings.Index($s, $_)]"}, {Line: 515, Value: "$s[bytes.Index($s, $_):]"}, {Line: 516, Value: "$s[:bytes.Index($s, $_)]"}, }, ReportTemplate: "Index() can return -1; maybe you wanted to do Index()+1", }, }, }, { Line: 524, Name: "unslice", MatcherName: "m", DocTags: []string{"style"}, DocSummary: "Detects slice expressions that can be simplified to sliced expression itself", DocBefore: "copy(b[:], values...)", DocAfter: "copy(b, values...)", Rules: []ir.Rule{{ Line: 525, SyntaxPatterns: []ir.PatternString{{Line: 525, Value: "$s[:]"}}, ReportTemplate: "could simplify $$ to $s", SuggestTemplate: "$s", WhereExpr: ir.FilterExpr{ Line: 526, Op: ir.FilterOrOp, Src: "m[\"s\"].Type.Is(`string`) || m[\"s\"].Type.Is(`[]$_`)", Args: []ir.FilterExpr{ { Line: 526, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`string`)", Value: "s", Args: []ir.FilterExpr{{Line: 526, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, { Line: 526, Op: ir.FilterVarTypeIsOp, Src: "m[\"s\"].Type.Is(`[]$_`)", Value: "s", Args: []ir.FilterExpr{{Line: 526, Op: ir.FilterStringOp, Src: "`[]$_`", Value: "[]$_"}}, }, }, }, }}, }, { Line: 535, Name: "yodaStyleExpr", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects Yoda style expressions and suggests to replace them", DocBefore: "return nil != ptr", DocAfter: "return ptr != nil", Rules: []ir.Rule{ { Line: 536, SyntaxPatterns: []ir.PatternString{{Line: 536, Value: "$constval != $x"}}, ReportTemplate: "consider to change order in expression to $x != $constval", WhereExpr: ir.FilterExpr{ Line: 536, Op: ir.FilterAndOp, Src: "m[\"constval\"].Node.Is(`BasicLit`) && !m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{ { Line: 536, Op: ir.FilterVarNodeIsOp, Src: "m[\"constval\"].Node.Is(`BasicLit`)", Value: "constval", Args: []ir.FilterExpr{{Line: 536, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }, { Line: 536, Op: ir.FilterNotOp, Src: "!m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{{ Line: 536, Op: ir.FilterVarNodeIsOp, Src: "m[\"x\"].Node.Is(`BasicLit`)", Value: "x", Args: []ir.FilterExpr{{Line: 536, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }}, }, }, }, }, { Line: 538, SyntaxPatterns: []ir.PatternString{{Line: 538, Value: "$constval == $x"}}, ReportTemplate: "consider to change order in expression to $x == $constval", WhereExpr: ir.FilterExpr{ Line: 538, Op: ir.FilterAndOp, Src: "m[\"constval\"].Node.Is(`BasicLit`) && !m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{ { Line: 538, Op: ir.FilterVarNodeIsOp, Src: "m[\"constval\"].Node.Is(`BasicLit`)", Value: "constval", Args: []ir.FilterExpr{{Line: 538, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }, { Line: 538, Op: ir.FilterNotOp, Src: "!m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{{ Line: 538, Op: ir.FilterVarNodeIsOp, Src: "m[\"x\"].Node.Is(`BasicLit`)", Value: "x", Args: []ir.FilterExpr{{Line: 538, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }}, }, }, }, }, { Line: 541, SyntaxPatterns: []ir.PatternString{{Line: 541, Value: "nil != $x"}}, ReportTemplate: "consider to change order in expression to $x != nil", WhereExpr: ir.FilterExpr{ Line: 541, Op: ir.FilterNotOp, Src: "!m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{{ Line: 541, Op: ir.FilterVarNodeIsOp, Src: "m[\"x\"].Node.Is(`BasicLit`)", Value: "x", Args: []ir.FilterExpr{{Line: 541, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }}, }, }, { Line: 543, SyntaxPatterns: []ir.PatternString{{Line: 543, Value: "nil == $x"}}, ReportTemplate: "consider to change order in expression to $x == nil", WhereExpr: ir.FilterExpr{ Line: 543, Op: ir.FilterNotOp, Src: "!m[\"x\"].Node.Is(`BasicLit`)", Args: []ir.FilterExpr{{ Line: 543, Op: ir.FilterVarNodeIsOp, Src: "m[\"x\"].Node.Is(`BasicLit`)", Value: "x", Args: []ir.FilterExpr{{Line: 543, Op: ir.FilterStringOp, Src: "`BasicLit`", Value: "BasicLit"}}, }}, }, }, }, }, { Line: 551, Name: "equalFold", MatcherName: "m", DocTags: []string{"performance", "experimental"}, DocSummary: "Detects unoptimal strings/bytes case-insensitive comparison", DocBefore: "strings.ToLower(x) == strings.ToLower(y)", DocAfter: "strings.EqualFold(x, y)", Rules: []ir.Rule{ { Line: 560, SyntaxPatterns: []ir.PatternString{ {Line: 561, Value: "strings.ToLower($x) == $y"}, {Line: 562, Value: "strings.ToLower($x) == strings.ToLower($y)"}, {Line: 563, Value: "$x == strings.ToLower($y)"}, {Line: 564, Value: "strings.ToUpper($x) == $y"}, {Line: 565, Value: "strings.ToUpper($x) == strings.ToUpper($y)"}, {Line: 566, Value: "$x == strings.ToUpper($y)"}, }, ReportTemplate: "consider replacing with strings.EqualFold($x, $y)", SuggestTemplate: "strings.EqualFold($x, $y)", WhereExpr: ir.FilterExpr{ Line: 567, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure && m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ { Line: 567, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure", Args: []ir.FilterExpr{ {Line: 567, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, {Line: 567, Op: ir.FilterVarPureOp, Src: "m[\"y\"].Pure", Value: "y"}, }, }, { Line: 567, Op: ir.FilterNeqOp, Src: "m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ {Line: 567, Op: ir.FilterVarTextOp, Src: "m[\"x\"].Text", Value: "x"}, {Line: 567, Op: ir.FilterVarTextOp, Src: "m[\"y\"].Text", Value: "y"}, }, }, }, }, }, { Line: 572, SyntaxPatterns: []ir.PatternString{ {Line: 573, Value: "strings.ToLower($x) != $y"}, {Line: 574, Value: "strings.ToLower($x) != strings.ToLower($y)"}, {Line: 575, Value: "$x != strings.ToLower($y)"}, {Line: 576, Value: "strings.ToUpper($x) != $y"}, {Line: 577, Value: "strings.ToUpper($x) != strings.ToUpper($y)"}, {Line: 578, Value: "$x != strings.ToUpper($y)"}, }, ReportTemplate: "consider replacing with !strings.EqualFold($x, $y)", SuggestTemplate: "!strings.EqualFold($x, $y)", WhereExpr: ir.FilterExpr{ Line: 579, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure && m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ { Line: 579, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure", Args: []ir.FilterExpr{ {Line: 579, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, {Line: 579, Op: ir.FilterVarPureOp, Src: "m[\"y\"].Pure", Value: "y"}, }, }, { Line: 579, Op: ir.FilterNeqOp, Src: "m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ {Line: 579, Op: ir.FilterVarTextOp, Src: "m[\"x\"].Text", Value: "x"}, {Line: 579, Op: ir.FilterVarTextOp, Src: "m[\"y\"].Text", Value: "y"}, }, }, }, }, }, { Line: 584, SyntaxPatterns: []ir.PatternString{ {Line: 585, Value: "bytes.Equal(bytes.ToLower($x), $y)"}, {Line: 586, Value: "bytes.Equal(bytes.ToLower($x), bytes.ToLower($y))"}, {Line: 587, Value: "bytes.Equal($x, bytes.ToLower($y))"}, {Line: 588, Value: "bytes.Equal(bytes.ToUpper($x), $y)"}, {Line: 589, Value: "bytes.Equal(bytes.ToUpper($x), bytes.ToUpper($y))"}, {Line: 590, Value: "bytes.Equal($x, bytes.ToUpper($y))"}, }, ReportTemplate: "consider replacing with bytes.EqualFold($x, $y)", SuggestTemplate: "bytes.EqualFold($x, $y)", WhereExpr: ir.FilterExpr{ Line: 591, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure && m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ { Line: 591, Op: ir.FilterAndOp, Src: "m[\"x\"].Pure && m[\"y\"].Pure", Args: []ir.FilterExpr{ {Line: 591, Op: ir.FilterVarPureOp, Src: "m[\"x\"].Pure", Value: "x"}, {Line: 591, Op: ir.FilterVarPureOp, Src: "m[\"y\"].Pure", Value: "y"}, }, }, { Line: 591, Op: ir.FilterNeqOp, Src: "m[\"x\"].Text != m[\"y\"].Text", Args: []ir.FilterExpr{ {Line: 591, Op: ir.FilterVarTextOp, Src: "m[\"x\"].Text", Value: "x"}, {Line: 591, Op: ir.FilterVarTextOp, Src: "m[\"y\"].Text", Value: "y"}, }, }, }, }, }, }, }, { Line: 600, Name: "argOrder", MatcherName: "m", DocTags: []string{"diagnostic"}, DocSummary: "Detects suspicious arguments order", DocBefore: "strings.HasPrefix(\"#\", userpass)", DocAfter: "strings.HasPrefix(userpass, \"#\")", Rules: []ir.Rule{{ Line: 601, SyntaxPatterns: []ir.PatternString{ {Line: 602, Value: "strings.HasPrefix($lit, $s)"}, {Line: 603, Value: "bytes.HasPrefix($lit, $s)"}, {Line: 604, Value: "strings.HasSuffix($lit, $s)"}, {Line: 605, Value: "bytes.HasSuffix($lit, $s)"}, {Line: 606, Value: "strings.Contains($lit, $s)"}, {Line: 607, Value: "bytes.Contains($lit, $s)"}, {Line: 608, Value: "strings.TrimPrefix($lit, $s)"}, {Line: 609, Value: "bytes.TrimPrefix($lit, $s)"}, {Line: 610, Value: "strings.TrimSuffix($lit, $s)"}, {Line: 611, Value: "bytes.TrimSuffix($lit, $s)"}, {Line: 612, Value: "strings.Split($lit, $s)"}, {Line: 613, Value: "bytes.Split($lit, $s)"}, }, ReportTemplate: "$lit and $s arguments order looks reversed", WhereExpr: ir.FilterExpr{ Line: 614, Op: ir.FilterAndOp, Src: "(m[\"lit\"].Const || m[\"lit\"].ConstSlice) &&\n\t!(m[\"s\"].Const || m[\"s\"].ConstSlice) &&\n\t!m[\"lit\"].Node.Is(`Ident`)", Args: []ir.FilterExpr{ { Line: 614, Op: ir.FilterAndOp, Src: "(m[\"lit\"].Const || m[\"lit\"].ConstSlice) &&\n\t!(m[\"s\"].Const || m[\"s\"].ConstSlice)", Args: []ir.FilterExpr{ { Line: 614, Op: ir.FilterOrOp, Src: "(m[\"lit\"].Const || m[\"lit\"].ConstSlice)", Args: []ir.FilterExpr{ { Line: 614, Op: ir.FilterVarConstOp, Src: "m[\"lit\"].Const", Value: "lit", }, { Line: 614, Op: ir.FilterVarConstSliceOp, Src: "m[\"lit\"].ConstSlice", Value: "lit", }, }, }, { Line: 615, Op: ir.FilterNotOp, Src: "!(m[\"s\"].Const || m[\"s\"].ConstSlice)", Args: []ir.FilterExpr{{ Line: 615, Op: ir.FilterOrOp, Src: "(m[\"s\"].Const || m[\"s\"].ConstSlice)", Args: []ir.FilterExpr{ { Line: 615, Op: ir.FilterVarConstOp, Src: "m[\"s\"].Const", Value: "s", }, { Line: 615, Op: ir.FilterVarConstSliceOp, Src: "m[\"s\"].ConstSlice", Value: "s", }, }, }}, }, }, }, { Line: 616, Op: ir.FilterNotOp, Src: "!m[\"lit\"].Node.Is(`Ident`)", Args: []ir.FilterExpr{{ Line: 616, Op: ir.FilterVarNodeIsOp, Src: "m[\"lit\"].Node.Is(`Ident`)", Value: "lit", Args: []ir.FilterExpr{{Line: 616, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, }}, }, }, }, }}, }, { Line: 624, Name: "stringConcatSimplify", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects string concat operations that can be simplified", DocBefore: "strings.Join([]string{x, y}, \"_\")", DocAfter: "x + \"_\" + y", Rules: []ir.Rule{ { Line: 625, SyntaxPatterns: []ir.PatternString{{Line: 625, Value: "strings.Join([]string{$x, $y}, \"\")"}}, ReportTemplate: "suggestion: $x + $y", SuggestTemplate: "$x + $y", }, { Line: 626, SyntaxPatterns: []ir.PatternString{{Line: 626, Value: "strings.Join([]string{$x, $y, $z}, \"\")"}}, ReportTemplate: "suggestion: $x + $y + $z", SuggestTemplate: "$x + $y + $z", }, { Line: 627, SyntaxPatterns: []ir.PatternString{{Line: 627, Value: "strings.Join([]string{$x, $y}, $glue)"}}, ReportTemplate: "suggestion: $x + $glue + $y", SuggestTemplate: "$x + $glue + $y", }, }, }, { Line: 634, Name: "timeExprSimplify", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects manual conversion to milli- or microseconds", DocBefore: "t.Unix() / 1000", DocAfter: "t.UnixMilli()", Rules: []ir.Rule{ { Line: 639, SyntaxPatterns: []ir.PatternString{{Line: 639, Value: "$t.Unix() / 1000"}}, ReportTemplate: "use $t.UnixMilli() instead of $$", SuggestTemplate: "$t.UnixMilli()", WhereExpr: ir.FilterExpr{ Line: 640, Op: ir.FilterAndOp, Src: "m.GoVersion().GreaterEqThan(\"1.17\") && isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 640, Op: ir.FilterGoVersionGreaterEqThanOp, Src: "m.GoVersion().GreaterEqThan(\"1.17\")", Value: "1.17", }, { Line: 640, Op: ir.FilterOrOp, Src: "isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 640, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 636, Op: ir.FilterStringOp, Src: "`time.Time`", Value: "time.Time"}}, }, { Line: 640, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`*time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 636, Op: ir.FilterStringOp, Src: "`*time.Time`", Value: "*time.Time"}}, }, }, }, }, }, }, { Line: 644, SyntaxPatterns: []ir.PatternString{{Line: 644, Value: "$t.UnixNano() * 1000"}}, ReportTemplate: "use $t.UnixMicro() instead of $$", SuggestTemplate: "$t.UnixMicro()", WhereExpr: ir.FilterExpr{ Line: 645, Op: ir.FilterAndOp, Src: "m.GoVersion().GreaterEqThan(\"1.17\") && isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 645, Op: ir.FilterGoVersionGreaterEqThanOp, Src: "m.GoVersion().GreaterEqThan(\"1.17\")", Value: "1.17", }, { Line: 645, Op: ir.FilterOrOp, Src: "isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 645, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 636, Op: ir.FilterStringOp, Src: "`time.Time`", Value: "time.Time"}}, }, { Line: 645, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`*time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 636, Op: ir.FilterStringOp, Src: "`*time.Time`", Value: "*time.Time"}}, }, }, }, }, }, }, }, }, { Line: 654, Name: "timeCmpSimplify", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects Before/After call of time.Time that can be simplified", DocBefore: "!t.Before(tt)", DocAfter: "t.After(tt)", Rules: []ir.Rule{ { Line: 659, SyntaxPatterns: []ir.PatternString{{Line: 659, Value: "!$t.Before($tt)"}}, ReportTemplate: "suggestion: $t.After($tt)", SuggestTemplate: "$t.After($tt)", WhereExpr: ir.FilterExpr{ Line: 660, Op: ir.FilterOrOp, Src: "isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 660, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 656, Op: ir.FilterStringOp, Src: "`time.Time`", Value: "time.Time"}}, }, { Line: 660, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`*time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 656, Op: ir.FilterStringOp, Src: "`*time.Time`", Value: "*time.Time"}}, }, }, }, }, { Line: 663, SyntaxPatterns: []ir.PatternString{{Line: 663, Value: "!$t.After($tt)"}}, ReportTemplate: "suggestion: $t.Before($tt)", SuggestTemplate: "$t.Before($tt)", WhereExpr: ir.FilterExpr{ Line: 664, Op: ir.FilterOrOp, Src: "isTime(m[\"t\"])", Args: []ir.FilterExpr{ { Line: 664, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 656, Op: ir.FilterStringOp, Src: "`time.Time`", Value: "time.Time"}}, }, { Line: 664, Op: ir.FilterVarTypeIsOp, Src: "m[\"t\"].Type.Is(`*time.Time`)", Value: "t", Args: []ir.FilterExpr{{Line: 656, Op: ir.FilterStringOp, Src: "`*time.Time`", Value: "*time.Time"}}, }, }, }, }, }, }, { Line: 672, Name: "exposedSyncMutex", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects exposed methods from sync.Mutex and sync.RWMutex", DocBefore: "type Foo struct{ ...; sync.Mutex; ... }", DocAfter: "type Foo struct{ ...; mu sync.Mutex; ... }", Rules: []ir.Rule{ { Line: 677, SyntaxPatterns: []ir.PatternString{{Line: 677, Value: "type $x struct { $*_; sync.Mutex; $*_ }"}}, ReportTemplate: "don't embed sync.Mutex", WhereExpr: ir.FilterExpr{ Line: 678, Op: ir.FilterVarTextMatchesOp, Src: "isExported(m[\"x\"])", Value: "x", Args: []ir.FilterExpr{{Line: 674, Op: ir.FilterStringOp, Src: "`^\\p{Lu}`", Value: "^\\p{Lu}"}}, }, }, { Line: 681, SyntaxPatterns: []ir.PatternString{{Line: 681, Value: "type $x struct { $*_; *sync.Mutex; $*_ }"}}, ReportTemplate: "don't embed *sync.Mutex", WhereExpr: ir.FilterExpr{ Line: 682, Op: ir.FilterVarTextMatchesOp, Src: "isExported(m[\"x\"])", Value: "x", Args: []ir.FilterExpr{{Line: 674, Op: ir.FilterStringOp, Src: "`^\\p{Lu}`", Value: "^\\p{Lu}"}}, }, }, { Line: 685, SyntaxPatterns: []ir.PatternString{{Line: 685, Value: "type $x struct { $*_; sync.RWMutex; $*_ }"}}, ReportTemplate: "don't embed sync.RWMutex", WhereExpr: ir.FilterExpr{ Line: 686, Op: ir.FilterVarTextMatchesOp, Src: "isExported(m[\"x\"])", Value: "x", Args: []ir.FilterExpr{{Line: 674, Op: ir.FilterStringOp, Src: "`^\\p{Lu}`", Value: "^\\p{Lu}"}}, }, }, { Line: 689, SyntaxPatterns: []ir.PatternString{{Line: 689, Value: "type $x struct { $*_; *sync.RWMutex; $*_ }"}}, ReportTemplate: "don't embed *sync.RWMutex", WhereExpr: ir.FilterExpr{ Line: 690, Op: ir.FilterVarTextMatchesOp, Src: "isExported(m[\"x\"])", Value: "x", Args: []ir.FilterExpr{{Line: 674, Op: ir.FilterStringOp, Src: "`^\\p{Lu}`", Value: "^\\p{Lu}"}}, }, }, }, }, { Line: 698, Name: "badSorting", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects bad usage of sort package", DocBefore: "xs = sort.StringSlice(xs)", DocAfter: "sort.Strings(xs)", Rules: []ir.Rule{ { Line: 699, SyntaxPatterns: []ir.PatternString{{Line: 699, Value: "$x = sort.IntSlice($x)"}}, ReportTemplate: "suspicious sort.IntSlice usage, maybe sort.Ints was intended?", SuggestTemplate: "sort.Ints($x)", WhereExpr: ir.FilterExpr{ Line: 700, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]int`)", Value: "x", Args: []ir.FilterExpr{{Line: 700, Op: ir.FilterStringOp, Src: "`[]int`", Value: "[]int"}}, }, }, { Line: 704, SyntaxPatterns: []ir.PatternString{{Line: 704, Value: "$x = sort.Float64Slice($x)"}}, ReportTemplate: "suspicious sort.Float64s usage, maybe sort.Float64s was intended?", SuggestTemplate: "sort.Float64s($x)", WhereExpr: ir.FilterExpr{ Line: 705, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]float64`)", Value: "x", Args: []ir.FilterExpr{{Line: 705, Op: ir.FilterStringOp, Src: "`[]float64`", Value: "[]float64"}}, }, }, { Line: 709, SyntaxPatterns: []ir.PatternString{{Line: 709, Value: "$x = sort.StringSlice($x)"}}, ReportTemplate: "suspicious sort.StringSlice usage, maybe sort.Strings was intended?", SuggestTemplate: "sort.Strings($x)", WhereExpr: ir.FilterExpr{ Line: 710, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`[]string`)", Value: "x", Args: []ir.FilterExpr{{Line: 710, Op: ir.FilterStringOp, Src: "`[]string`", Value: "[]string"}}, }, }, }, }, { Line: 719, Name: "externalErrorReassign", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects suspicious reassigment of error from another package", DocBefore: "io.EOF = nil", DocAfter: "/* don't do it */", Rules: []ir.Rule{{ Line: 720, SyntaxPatterns: []ir.PatternString{{Line: 720, Value: "$pkg.$err = $x"}}, ReportTemplate: "suspicious reassigment of error from another package", WhereExpr: ir.FilterExpr{ Line: 721, Op: ir.FilterAndOp, Src: "m[\"err\"].Type.Is(`error`) && m[\"pkg\"].Object.Is(`PkgName`)", Args: []ir.FilterExpr{ { Line: 721, Op: ir.FilterVarTypeIsOp, Src: "m[\"err\"].Type.Is(`error`)", Value: "err", Args: []ir.FilterExpr{{Line: 721, Op: ir.FilterStringOp, Src: "`error`", Value: "error"}}, }, { Line: 721, Op: ir.FilterVarObjectIsOp, Src: "m[\"pkg\"].Object.Is(`PkgName`)", Value: "pkg", Args: []ir.FilterExpr{{Line: 721, Op: ir.FilterStringOp, Src: "`PkgName`", Value: "PkgName"}}, }, }, }, }}, }, { Line: 729, Name: "emptyDecl", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects suspicious empty declarations blocks", DocBefore: "var()", DocAfter: "/* nothing */", Rules: []ir.Rule{ { Line: 730, SyntaxPatterns: []ir.PatternString{{Line: 730, Value: "var()"}}, ReportTemplate: "empty var() block", }, { Line: 731, SyntaxPatterns: []ir.PatternString{{Line: 731, Value: "const()"}}, ReportTemplate: "empty const() block", }, { Line: 732, SyntaxPatterns: []ir.PatternString{{Line: 732, Value: "type()"}}, ReportTemplate: "empty type() block", }, }, }, { Line: 739, Name: "dynamicFmtString", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects suspicious formatting strings usage", DocBefore: "fmt.Errorf(msg)", DocAfter: "fmt.Errorf(\"%s\", msg)", Rules: []ir.Rule{ { Line: 740, SyntaxPatterns: []ir.PatternString{{Line: 740, Value: "fmt.Errorf($f)"}}, ReportTemplate: "use errors.New($f) or fmt.Errorf(\"%s\", $f) instead", SuggestTemplate: "errors.New($f)", WhereExpr: ir.FilterExpr{ Line: 741, Op: ir.FilterNotOp, Src: "!m[\"f\"].Const", Args: []ir.FilterExpr{{ Line: 741, Op: ir.FilterVarConstOp, Src: "m[\"f\"].Const", Value: "f", }}, }, }, { Line: 745, SyntaxPatterns: []ir.PatternString{{Line: 745, Value: "fmt.Errorf($f($*args))"}}, ReportTemplate: "use errors.New($f($*args)) or fmt.Errorf(\"%s\", $f($*args)) instead", SuggestTemplate: "errors.New($f($*args))", }, }, }, { Line: 754, Name: "stringsCompare", MatcherName: "m", DocTags: []string{"style", "experimental"}, DocSummary: "Detects strings.Compare usage", DocBefore: "strings.Compare(x, y)", DocAfter: "x < y", Rules: []ir.Rule{ { Line: 755, SyntaxPatterns: []ir.PatternString{{Line: 755, Value: "strings.Compare($s1, $s2) == 0"}}, ReportTemplate: "suggestion: $s1 == $s2", SuggestTemplate: "$s1 == $s2", }, { Line: 758, SyntaxPatterns: []ir.PatternString{ {Line: 758, Value: "strings.Compare($s1, $s2) == -1"}, {Line: 759, Value: "strings.Compare($s1, $s2) < 0"}, }, ReportTemplate: "suggestion: $s1 < $s2", SuggestTemplate: "$s1 < $s2", }, { Line: 762, SyntaxPatterns: []ir.PatternString{ {Line: 762, Value: "strings.Compare($s1, $s2) == 1"}, {Line: 763, Value: "strings.Compare($s1, $s2) > 0"}, }, ReportTemplate: "suggestion: $s1 > $s2", SuggestTemplate: "$s1 > $s2", }, }, }, { Line: 771, Name: "uncheckedInlineErr", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects unchecked errors in if statements", DocBefore: "if err := expr(); err2 != nil { /*...*/ }", DocAfter: "if err := expr(); err != nil { /*...*/ }", Rules: []ir.Rule{{ Line: 772, SyntaxPatterns: []ir.PatternString{ {Line: 773, Value: "if $err := $_($*_); $err2 != nil { $*_ }"}, {Line: 774, Value: "if $err = $_($*_); $err2 != nil { $*_ }"}, {Line: 775, Value: "if $*_, $err := $_($*_); $err2 != nil { $*_ }"}, {Line: 776, Value: "if $*_, $err = $_($*_); $err2 != nil { $*_ }"}, }, ReportTemplate: "$err error is unchecked, maybe intended to check it instead of $err2", WhereExpr: ir.FilterExpr{ Line: 777, Op: ir.FilterAndOp, Src: "m[\"err\"].Type.Implements(\"error\") && m[\"err2\"].Type.Implements(\"error\") &&\n\tm[\"err\"].Text != m[\"err2\"].Text", Args: []ir.FilterExpr{ { Line: 777, Op: ir.FilterAndOp, Src: "m[\"err\"].Type.Implements(\"error\") && m[\"err2\"].Type.Implements(\"error\")", Args: []ir.FilterExpr{ { Line: 777, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"err\"].Type.Implements(\"error\")", Value: "err", Args: []ir.FilterExpr{{Line: 777, Op: ir.FilterStringOp, Src: "\"error\"", Value: "error"}}, }, { Line: 777, Op: ir.FilterVarTypeImplementsOp, Src: "m[\"err2\"].Type.Implements(\"error\")", Value: "err2", Args: []ir.FilterExpr{{Line: 777, Op: ir.FilterStringOp, Src: "\"error\"", Value: "error"}}, }, }, }, { Line: 778, Op: ir.FilterNeqOp, Src: "m[\"err\"].Text != m[\"err2\"].Text", Args: []ir.FilterExpr{ {Line: 778, Op: ir.FilterVarTextOp, Src: "m[\"err\"].Text", Value: "err"}, {Line: 778, Op: ir.FilterVarTextOp, Src: "m[\"err2\"].Text", Value: "err2"}, }, }, }, }, LocationVar: "err", }}, }, { Line: 787, Name: "sloppyTestFuncName", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, DocSummary: "Detects unsupported test and benchmark funcs", DocBefore: "func TessstUnit(t *testing.T)", DocAfter: "func TestUnit(t *testing.T)", Rules: []ir.Rule{ { Line: 788, SyntaxPatterns: []ir.PatternString{{Line: 788, Value: "func $test($_ *testing.T) { $*_ }"}}, ReportTemplate: "function $test should be of form TestXXX(t *testing.T)", WhereExpr: ir.FilterExpr{ Line: 789, Op: ir.FilterAndOp, Src: "!m[\"test\"].Text.Matches(\"Test.*\") &&\n\t!m[\"test\"].Text.Matches(\"test.*\")", Args: []ir.FilterExpr{ { Line: 789, Op: ir.FilterNotOp, Src: "!m[\"test\"].Text.Matches(\"Test.*\")", Args: []ir.FilterExpr{{ Line: 789, Op: ir.FilterVarTextMatchesOp, Src: "m[\"test\"].Text.Matches(\"Test.*\")", Value: "test", Args: []ir.FilterExpr{{Line: 789, Op: ir.FilterStringOp, Src: "\"Test.*\"", Value: "Test.*"}}, }}, }, { Line: 790, Op: ir.FilterNotOp, Src: "!m[\"test\"].Text.Matches(\"test.*\")", Args: []ir.FilterExpr{{ Line: 790, Op: ir.FilterVarTextMatchesOp, Src: "m[\"test\"].Text.Matches(\"test.*\")", Value: "test", Args: []ir.FilterExpr{{Line: 790, Op: ir.FilterStringOp, Src: "\"test.*\"", Value: "test.*"}}, }}, }, }, }, }, { Line: 793, SyntaxPatterns: []ir.PatternString{{Line: 793, Value: "func $bench($_ *testing.B) { $*_ }"}}, ReportTemplate: "function $bench should be of form BenchmarkXXX(b *testing.B)", WhereExpr: ir.FilterExpr{ Line: 794, Op: ir.FilterAndOp, Src: "!m[\"bench\"].Text.Matches(\"Benchmark.*\") &&\n\t!m[\"bench\"].Text.Matches(\"bench.*\")", Args: []ir.FilterExpr{ { Line: 794, Op: ir.FilterNotOp, Src: "!m[\"bench\"].Text.Matches(\"Benchmark.*\")", Args: []ir.FilterExpr{{ Line: 794, Op: ir.FilterVarTextMatchesOp, Src: "m[\"bench\"].Text.Matches(\"Benchmark.*\")", Value: "bench", Args: []ir.FilterExpr{{Line: 794, Op: ir.FilterStringOp, Src: "\"Benchmark.*\"", Value: "Benchmark.*"}}, }}, }, { Line: 795, Op: ir.FilterNotOp, Src: "!m[\"bench\"].Text.Matches(\"bench.*\")", Args: []ir.FilterExpr{{ Line: 795, Op: ir.FilterVarTextMatchesOp, Src: "m[\"bench\"].Text.Matches(\"bench.*\")", Value: "bench", Args: []ir.FilterExpr{{Line: 795, Op: ir.FilterStringOp, Src: "\"bench.*\"", Value: "bench.*"}}, }}, }, }, }, }, { Line: 798, SyntaxPatterns: []ir.PatternString{{Line: 798, Value: "func $test($_ *testing.T) { $*_ }"}}, ReportTemplate: "function $test looks like a test helper, consider to change 1st param to 'tb testing.TB'", WhereExpr: ir.FilterExpr{ Line: 799, Op: ir.FilterVarTextMatchesOp, Src: "m[\"test\"].Text.Matches(\"^test.*\")", Value: "test", Args: []ir.FilterExpr{{Line: 799, Op: ir.FilterStringOp, Src: "\"^test.*\"", Value: "^test.*"}}, }, }, { Line: 802, SyntaxPatterns: []ir.PatternString{{Line: 802, Value: "func $bench($_ *testing.B) { $*_ }"}}, ReportTemplate: "function $bench looks like a benchmark helper, consider to change 1st param to 'tb testing.TB'", WhereExpr: ir.FilterExpr{ Line: 803, Op: ir.FilterVarTextMatchesOp, Src: "m[\"bench\"].Text.Matches(\"^bench(mark)?.*\")", Value: "bench", Args: []ir.FilterExpr{{Line: 803, Op: ir.FilterStringOp, Src: "\"^bench(mark)?.*\"", Value: "^bench(mark)?.*"}}, }, }, }, }, }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.