diff --git a/checkers/octalLiteral_checker.go b/checkers/octalLiteral_checker.go new file mode 100644 index 000000000..e40ec6db5 --- /dev/null +++ b/checkers/octalLiteral_checker.go @@ -0,0 +1,82 @@ +package checkers + +import ( + "go/ast" + "go/token" + "go/types" + + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "octalLiteral" + info.Tags = []string{"diagnostic", "experimental"} + info.Summary = "Detects octal literals passed to functions" + info.Before = `foo(02)` + info.After = `foo(2)` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + c := &octalLiteralChecker{ + ctx: ctx, + octFriendlyPkg: map[string]bool{ + "os": true, + "io/ioutil": true, + }, + } + return astwalk.WalkerForExpr(c) + }) +} + +type octalLiteralChecker struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext + + octFriendlyPkg map[string]bool +} + +func (c *octalLiteralChecker) VisitExpr(expr ast.Expr) { + call := astcast.ToCallExpr(expr) + calledExpr := astcast.ToSelectorExpr(call.Fun) + ident := astcast.ToIdent(calledExpr.X) + + if obj, ok := c.ctx.TypesInfo.ObjectOf(ident).(*types.PkgName); ok { + pkg := obj.Imported() + if c.octFriendlyPkg[pkg.Path()] { + return + } + } + + for _, arg := range call.Args { + if lit := astcast.ToBasicLit(c.unsign(arg)); len(lit.Value) > 1 && + c.isIntLiteral(lit) && + c.isOctalLiteral(lit) { + c.warn(call) + return + } + } +} + +func (c *octalLiteralChecker) unsign(e ast.Expr) ast.Expr { + u, ok := e.(*ast.UnaryExpr) + if !ok { + return e + } + return u.X +} + +func (c *octalLiteralChecker) isIntLiteral(lit *ast.BasicLit) bool { + return lit.Kind == token.INT +} + +func (c *octalLiteralChecker) isOctalLiteral(lit *ast.BasicLit) bool { + return lit.Value[0] == '0' && + lit.Value[1] != 'x' && + lit.Value[1] != 'X' +} + +func (c *octalLiteralChecker) warn(expr ast.Expr) { + c.ctx.Warn(expr, "suspicious octal args in `%s`", expr) +} diff --git a/checkers/testdata/octalLiteral/negative_tests.go b/checkers/testdata/octalLiteral/negative_tests.go new file mode 100644 index 000000000..4e3a2126f --- /dev/null +++ b/checkers/testdata/octalLiteral/negative_tests.go @@ -0,0 +1,76 @@ +package checker_test + +import ( + "io/ioutil" + "log" + "math" + "os" +) + +func calculateInt(x int) int { + return x +} + +func calculateHex(x int) int { + return x +} + +func calculateFloat(x float64) float64 { + return x +} + +func calculateString(x string) string { + return x +} + +func calculateIntPair(x, y int) (int, int) { + return x, y +} + +func NoWarningsCalc() { + _ = calculateInt(0) + _ = calculateInt(1) + _ = calculateInt(+1) + _ = calculateInt(-1) + _ = calculateInt(12) + _ = calculateInt(1 + 2) + + var int x = 03 + _ = calculateInt(x) + + _ = calculateHex(0x0) + _ = calculateHex(0X42) + _ = calculateHex(0xAA1) + _ = calculateHex(-0xaa1) + + _ = calculateFloat(0.2) + _ = calculateFloat(+0.2) + _ = calculateFloat(-0.2) + + _ = calculateString("1") + _ = calculateString("01") + _ = calculateString("0.1") + + _ = calculateIntPair(1, 2) + _ = calculateIntPair(-1, 2) + _ = calculateIntPair(0, 2) + + _ = math.Exp(12) + _ = math.Exp(0x12) + _ = math.Max(12, 0xd) + _ = math.Min(0, 1) +} + +func NoWarningsOs() { + f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + log.Fatal(err) + } + if err := f.Close(); err != nil { + log.Fatal(err) + } +} + +func NoWarningsIoutil() { + _ = ioutil.WriteFile("notes.txt", []byte(), 0666) +} diff --git a/checkers/testdata/octalLiteral/positive_tests.go b/checkers/testdata/octalLiteral/positive_tests.go new file mode 100644 index 000000000..3c25e443c --- /dev/null +++ b/checkers/testdata/octalLiteral/positive_tests.go @@ -0,0 +1,77 @@ +package checker_test + +import ( + "math" +) + +func calculateInt(x int) int { + return x +} + +func calculateIntPair(x, y int) (int, int) { + return x, y +} + +func calculateManyArgs(x int, s string, y int) (int, string, int) { + return x, s, y +} + +func warningsCalc() { + /*! suspicious octal args in `calculateInt(00)` */ + _ = calculateInt(00) + + /*! suspicious octal args in `calculateInt(+01)` */ + _ = calculateInt(+01) + + /*! suspicious octal args in `calculateInt(-01)` */ + _ = calculateInt(-01) + + /*! suspicious octal args in `calculateInt(012)` */ + _ = calculateInt(calculateInt(012)) + + /*! suspicious octal args in `calculateIntPair(01, 2)` */ + _ = calculateIntPair(01, 2) + + /*! suspicious octal args in `calculateIntPair(-1, -012)` */ + _ = calculateIntPair(-1, -012) + + /*! suspicious octal args in `calculateIntPair(01, 02)` */ + _ = calculateIntPair(01, 02) + + /*! suspicious octal args in `calculateInt(01)` */ + /*! suspicious octal args in `calculateInt(02)` */ + _ = calculateIntPair(calculateInt(01), calculateInt(02)) + + /*! suspicious octal args in `calculateIntPair(01, calculateInt(02))` */ + /*! suspicious octal args in `calculateInt(02)` */ + _ = calculateIntPair(01, calculateInt(02)) + + /*! suspicious octal args in `calculateManyArgs(11, "12", 013)` */ + _ = calculateManyArgs(11, "12", 013) + + /*! suspicious octal args in `calculateManyArgs(-02, "3", -04)` */ + _ = calculateManyArgs(-02, "3", -04) + + /*! suspicious octal args in `math.Exp(012)` */ + _ = math.Exp(012) + + /*! suspicious octal args in `math.Max(12, 01)` */ + _ = math.Max(12, 01) + + /*! suspicious octal args in `math.Max(1, 01)` */ + _ = math.Max(1, math.Max(1, 01)) +} + +type OpenServer struct { + x int +} + +func (os *OpenServer) Init(x int) { + os.x = x +} + +func warningsOs() { + var os OpenServer + /*! suspicious octal args in `os.Init(02)` */ + os.Init(02) +}