Add staticcheck tool
This commit is contained in:
116
vendor/honnef.co/go/tools/staticcheck/sa4016/sa4016.go
vendored
Normal file
116
vendor/honnef.co/go/tools/staticcheck/sa4016/sa4016.go
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package sa4016
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/analysis/code"
|
||||
"honnef.co/go/tools/analysis/lint"
|
||||
"honnef.co/go/tools/analysis/report"
|
||||
"honnef.co/go/tools/go/ast/astutil"
|
||||
"honnef.co/go/tools/go/types/typeutil"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
)
|
||||
|
||||
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
|
||||
Analyzer: &analysis.Analyzer{
|
||||
Name: "SA4016",
|
||||
Run: run,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
},
|
||||
Doc: &lint.RawDocumentation{
|
||||
Title: `Certain bitwise operations, such as \'x ^ 0\', do not do anything useful`,
|
||||
Since: "2017.1",
|
||||
Severity: lint.SeverityWarning,
|
||||
MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants
|
||||
},
|
||||
})
|
||||
|
||||
var Analyzer = SCAnalyzer.Analyzer
|
||||
|
||||
func run(pass *analysis.Pass) (any, error) {
|
||||
fn := func(node ast.Node) {
|
||||
binop := node.(*ast.BinaryExpr)
|
||||
if !typeutil.All(pass.TypesInfo.TypeOf(binop), func(term *types.Term) bool {
|
||||
b, ok := term.Type().Underlying().(*types.Basic)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return (b.Info() & types.IsInteger) != 0
|
||||
}) {
|
||||
return
|
||||
}
|
||||
switch binop.Op {
|
||||
case token.AND, token.OR, token.XOR:
|
||||
default:
|
||||
// we do not flag shifts because too often, x<<0 is part
|
||||
// of a pattern, x<<0, x<<8, x<<16, ...
|
||||
return
|
||||
}
|
||||
if y, ok := binop.Y.(*ast.Ident); ok {
|
||||
obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if obj.Pkg() != pass.Pkg {
|
||||
// identifier was dot-imported
|
||||
return
|
||||
}
|
||||
if v, _ := constant.Int64Val(obj.Val()); v != 0 {
|
||||
return
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos())
|
||||
if len(path) < 2 {
|
||||
return
|
||||
}
|
||||
spec, ok := path[1].(*ast.ValueSpec)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if len(spec.Names) != 1 || len(spec.Values) != 1 {
|
||||
// TODO(dh): we could support this
|
||||
return
|
||||
}
|
||||
ident, ok := spec.Values[0].(*ast.Ident)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !isIota(pass.TypesInfo.ObjectOf(ident)) {
|
||||
return
|
||||
}
|
||||
switch binop.Op {
|
||||
case token.AND:
|
||||
report.Report(pass, node,
|
||||
fmt.Sprintf("%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))
|
||||
case token.OR, token.XOR:
|
||||
report.Report(pass, node,
|
||||
fmt.Sprintf("%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))
|
||||
}
|
||||
} else if code.IsIntegerLiteral(pass, binop.Y, constant.MakeInt64(0)) {
|
||||
switch binop.Op {
|
||||
case token.AND:
|
||||
report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop)))
|
||||
case token.OR, token.XOR:
|
||||
report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X)))
|
||||
}
|
||||
}
|
||||
}
|
||||
code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func isIota(obj types.Object) bool {
|
||||
if obj.Name() != "iota" {
|
||||
return false
|
||||
}
|
||||
c, ok := obj.(*types.Const)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return c.Pkg() == nil
|
||||
}
|
||||
Reference in New Issue
Block a user