diff --git a/.golangci.yml b/.golangci.yml index 7934cc7e2948..16bfba3e2ead 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,7 +31,6 @@ linters-settings: - experimental disabled-checks: - wrapperFunc - - commentFormatting # https://github.com/go-critic/go-critic/issues/755 linters: enable-all: true diff --git a/README.md b/README.md index 8ea78fa8b85d..70d74896ffc3 100644 --- a/README.md +++ b/README.md @@ -779,7 +779,6 @@ linters-settings: - experimental disabled-checks: - wrapperFunc - - commentFormatting # https://github.com/go-critic/go-critic/issues/755 linters: enable-all: true diff --git a/go.mod b/go.mod index 20ab1bf2214d..79daab9ff99c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/davecgh/go-spew v1.1.0 // indirect github.com/fatih/color v1.6.0 - github.com/go-critic/go-critic v0.0.0-20181204210945-0af0999fabfb + github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead github.com/go-lintpack/lintpack v0.5.2 github.com/go-ole/go-ole v1.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect diff --git a/go.sum b/go.sum index 579b13d17c9e..7a029979cf61 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/fatih/color v1.6.0 h1:66qjqZk8kalYAvDRtM1AdAJQI0tj4Wrue3Eq3B3pmFU= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-critic/go-critic v0.0.0-20181204210945-0af0999fabfb h1:faOtDYqSVJsFEJAW+SwEMvh7alhYsb42fER6tt8yXfA= -github.com/go-critic/go-critic v0.0.0-20181204210945-0af0999fabfb/go.mod h1:PSww+HOJZQ3TN2hi6sphNiW1PhwELxbsK8+Jy1sjML8= +github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead h1:qwmAYufKDopQnFdeMw+iHJVxAd2CbF+VFKHyJJwnPKk= +github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead/go.mod h1:3MzXZKJdeXqdU9cj+rvZdNiN7SZ8V9OjybF8loZDmHU= github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= @@ -96,6 +96,7 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936 h1:kw1v0NlnN+GZcU8Ma8CLF2Zzgjfx95gs3/GN3vYAPpo= diff --git a/vendor/github.com/go-critic/go-critic/LICENSE b/vendor/github.com/go-critic/go-critic/LICENSE index f677137d1d85..b944b4bbdbec 100644 --- a/vendor/github.com/go-critic/go-critic/LICENSE +++ b/vendor/github.com/go-critic/go-critic/LICENSE @@ -1,7 +1,7 @@ MIT License -Copyright (c) 2018 Alekseev Artem -Copyright (c) 2018 Ravil Bikbulatov +Copyright (c) 2018-2019 Alekseev Artem +Copyright (c) 2018-2019 Ravil Bikbulatov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go b/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go index a81fdab6e6d4..63f5d9fea4e6 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go @@ -4,9 +4,9 @@ import ( "go/ast" "go/token" - "github.com/go-critic/go-critic/checkers/internal/lintutil" "github.com/go-lintpack/lintpack" "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" "github.com/go-toolsmith/astequal" ) @@ -73,7 +73,7 @@ func (c *appendCombineChecker) matchAppend(stmt ast.Stmt, slice ast.Expr) *ast.C // xs are 0-N append arguments, but not variadic argument, // because it makes append combining impossible. - assign := lintutil.AsAssignStmt(stmt) + assign := astcast.ToAssignStmt(stmt) if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { return nil } diff --git a/vendor/github.com/go-critic/go-critic/checkers/argOrder_checker.go b/vendor/github.com/go-critic/go-critic/checkers/argOrder_checker.go new file mode 100644 index 000000000000..85a6f7c664ea --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/checkers/argOrder_checker.go @@ -0,0 +1,98 @@ +package checkers + +import ( + "go/ast" + "go/types" + + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" + "github.com/go-toolsmith/astcopy" + "github.com/go-toolsmith/astp" + "github.com/go-toolsmith/typep" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "argOrder" + info.Tags = []string{"diagnostic", "experimental"} + info.Summary = "Detects suspicious arguments order" + info.Before = `strings.HasPrefix("#", userpass)` + info.After = `strings.HasPrefix(userpass, "#")` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + return astwalk.WalkerForExpr(&argOrderChecker{ctx: ctx}) + }) +} + +type argOrderChecker struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext +} + +func (c *argOrderChecker) VisitExpr(expr ast.Expr) { + call := astcast.ToCallExpr(expr) + + // For now only handle functions of 2 args. + // TODO(Quasilyte): generalize the algorithm and add more patterns. + if len(call.Args) != 2 { + return + } + + calledExpr := astcast.ToSelectorExpr(call.Fun) + obj, ok := c.ctx.TypesInfo.ObjectOf(astcast.ToIdent(calledExpr.X)).(*types.PkgName) + if !ok || !isStdlibPkg(obj.Imported()) { + return + } + + x := call.Args[0] + y := call.Args[1] + switch calledExpr.Sel.Name { + case "HasPrefix", "HasSuffix", "Contains", "TrimPrefix", "TrimSuffix", "Split": + if obj.Name() != "bytes" && obj.Name() != "strings" { + return + } + if c.isConstLiteral(x) && !c.isConstLiteral(y) { + c.warn(call) + } + } +} + +func (c *argOrderChecker) isConstLiteral(x ast.Expr) bool { + if c.ctx.TypesInfo.Types[x].Value != nil { + return true + } + + // Also permit byte slices. + switch x := x.(type) { + case *ast.CallExpr: + // Handle `[]byte("abc")` as well. + if len(x.Args) != 1 || !astp.IsBasicLit(x.Args[0]) { + return false + } + typ, ok := c.ctx.TypesInfo.TypeOf(x.Fun).(*types.Slice) + return ok && typep.HasUint8Kind(typ.Elem()) + + case *ast.CompositeLit: + // Check if it's a const byte slice. + typ, ok := c.ctx.TypesInfo.TypeOf(x).(*types.Slice) + if !ok || !typep.HasUint8Kind(typ.Elem()) { + return false + } + for _, elt := range x.Elts { + if !astp.IsBasicLit(elt) { + return false + } + } + return true + + default: + return false + } +} + +func (c *argOrderChecker) warn(call *ast.CallExpr) { + fixed := astcopy.CallExpr(call) + fixed.Args[0], fixed.Args[1] = fixed.Args[1], fixed.Args[0] + c.ctx.Warn(call, "probably meant `%s`", fixed) +} diff --git a/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go b/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go index 65b08143a401..81fbc2db9779 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go @@ -1,14 +1,18 @@ package checkers import ( + "fmt" "go/ast" "go/token" + "strconv" "github.com/go-critic/go-critic/checkers/internal/lintutil" "github.com/go-lintpack/lintpack" "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" "github.com/go-toolsmith/astcopy" "github.com/go-toolsmith/astequal" + "github.com/go-toolsmith/astp" "github.com/go-toolsmith/typep" "golang.org/x/tools/go/ast/astutil" ) @@ -37,6 +41,10 @@ type boolExprSimplifyChecker struct { } func (c *boolExprSimplifyChecker) VisitExpr(x ast.Expr) { + if !astp.IsBinaryExpr(x) && !astp.IsUnaryExpr(x) { + return + } + // Throw away non-bool expressions and avoid redundant // AST copying below. if typ := c.ctx.TypesInfo.TypeOf(x); typ == nil || !typep.HasBoolKind(typ.Underlying()) { @@ -65,14 +73,16 @@ func (c *boolExprSimplifyChecker) simplifyBool(x ast.Expr) ast.Expr { c.negatedEquals(cur) || c.invertComparison(cur) || c.combineChecks(cur) || + c.removeIncDec(cur) || + c.foldRanges(cur) || true }).(ast.Expr) } func (c *boolExprSimplifyChecker) doubleNegation(cur *astutil.Cursor) bool { - neg1 := lintutil.AsUnaryExprOp(cur.Node(), token.NOT) - neg2 := lintutil.AsUnaryExprOp(astutil.Unparen(neg1.X), token.NOT) - if !lintutil.IsNil(neg1) && !lintutil.IsNil(neg2) { + neg1 := astcast.ToUnaryExpr(cur.Node()) + neg2 := astcast.ToUnaryExpr(astutil.Unparen(neg1.X)) + if neg1.Op == token.NOT && neg2.Op == token.NOT { cur.Replace(astutil.Unparen(neg2.X)) return true } @@ -84,9 +94,9 @@ func (c *boolExprSimplifyChecker) negatedEquals(cur *astutil.Cursor) bool { if !ok || x.Op != token.EQL { return false } - neg1 := lintutil.AsUnaryExprOp(x.X, token.NOT) - neg2 := lintutil.AsUnaryExprOp(x.Y, token.NOT) - if !lintutil.IsNil(neg1) && !lintutil.IsNil(neg2) { + neg1 := astcast.ToUnaryExpr(x.X) + neg2 := astcast.ToUnaryExpr(x.Y) + if neg1.Op == token.NOT && neg2.Op == token.NOT { x.X = neg1.X x.Y = neg2.X return true @@ -99,9 +109,9 @@ func (c *boolExprSimplifyChecker) invertComparison(cur *astutil.Cursor) bool { return false } - neg := lintutil.AsUnaryExprOp(cur.Node(), token.NOT) - cmp := lintutil.AsBinaryExpr(astutil.Unparen(neg.X)) - if lintutil.IsNil(neg) || lintutil.IsNil(cmp) { + neg := astcast.ToUnaryExpr(cur.Node()) + cmp := astcast.ToBinaryExpr(astutil.Unparen(neg.X)) + if neg.Op != token.NOT { return false } @@ -127,17 +137,23 @@ func (c *boolExprSimplifyChecker) invertComparison(cur *astutil.Cursor) bool { return true } +func (c *boolExprSimplifyChecker) isSafe(x ast.Expr) bool { + return typep.SideEffectFree(c.ctx.TypesInfo, x) +} + func (c *boolExprSimplifyChecker) combineChecks(cur *astutil.Cursor) bool { - or := lintutil.AsBinaryExprOp(cur.Node(), token.LOR) - lhs := lintutil.AsBinaryExpr(astutil.Unparen(or.X)) - rhs := lintutil.AsBinaryExpr(astutil.Unparen(or.Y)) + or, ok := cur.Node().(*ast.BinaryExpr) + if !ok || or.Op != token.LOR { + return false + } + + lhs := astcast.ToBinaryExpr(astutil.Unparen(or.X)) + rhs := astcast.ToBinaryExpr(astutil.Unparen(or.Y)) if !astequal.Expr(lhs.X, rhs.X) || !astequal.Expr(lhs.Y, rhs.Y) { return false } - safe := typep.SideEffectFree(c.ctx.TypesInfo, lhs.X) && - typep.SideEffectFree(c.ctx.TypesInfo, lhs.Y) - if !safe { + if !c.isSafe(lhs.X) || !c.isSafe(lhs.Y) { return false } @@ -161,6 +177,163 @@ func (c *boolExprSimplifyChecker) combineChecks(cur *astutil.Cursor) bool { return false } +func (c *boolExprSimplifyChecker) removeIncDec(cur *astutil.Cursor) bool { + cmp := astcast.ToBinaryExpr(cur.Node()) + + matchOneWay := func(op token.Token, x, y *ast.BinaryExpr) bool { + if x.Op != op || astcast.ToBasicLit(x.Y).Value != "1" { + return false + } + if y.Op == op && astcast.ToBasicLit(y.Y).Value == "1" { + return false + } + return true + } + replace := func(lhsOp, rhsOp, replacement token.Token) bool { + lhs := astcast.ToBinaryExpr(cmp.X) + rhs := astcast.ToBinaryExpr(cmp.Y) + switch { + case matchOneWay(lhsOp, lhs, rhs): + cmp.X = lhs.X + cmp.Op = replacement + cur.Replace(cmp) + return true + case matchOneWay(rhsOp, rhs, lhs): + cmp.Y = rhs.X + cmp.Op = replacement + cur.Replace(cmp) + return true + default: + return false + } + } + + switch cmp.Op { + case token.GTR: + // `x > y-1` => `x >= y` + // `x+1 > y` => `x >= y` + return replace(token.ADD, token.SUB, token.GEQ) + + case token.GEQ: + // `x >= y+1` => `x > y` + // `x-1 >= y` => `x > y` + return replace(token.SUB, token.ADD, token.GTR) + + case token.LSS: + // `x < y+1` => `x <= y` + // `x-1 < y` => `x <= y` + return replace(token.SUB, token.ADD, token.LEQ) + + case token.LEQ: + // `x <= y-1` => `x < y` + // `x+1 <= y` => `x < y` + return replace(token.ADD, token.SUB, token.LSS) + + default: + return false + } +} + +func (c *boolExprSimplifyChecker) foldRanges(cur *astutil.Cursor) bool { + e, ok := cur.Node().(*ast.BinaryExpr) + if !ok { + return false + } + lhs := astcast.ToBinaryExpr(e.X) + rhs := astcast.ToBinaryExpr(e.Y) + if !c.isSafe(lhs.X) || !c.isSafe(rhs.X) { + return false + } + if !astequal.Expr(lhs.X, rhs.X) { + return false + } + + c1, ok := c.int64val(lhs.Y) + if !ok { + return false + } + c2, ok := c.int64val(rhs.Y) + if !ok { + return false + } + + type combination struct { + lhsOp token.Token + rhsOp token.Token + rhsDiff int64 + resDelta int64 + } + match := func(comb *combination) bool { + if lhs.Op != comb.lhsOp || rhs.Op != comb.rhsOp { + return false + } + if c2-c1 != comb.rhsDiff { + return false + } + return true + } + + switch e.Op { + case token.LAND: + combTable := [...]combination{ + // `x > c && x < c+2` => `x == c+1` + {token.GTR, token.LSS, 2, 1}, + // `x >= c && x < c+1` => `x == c` + {token.GEQ, token.LSS, 1, 0}, + // `x > c && x <= c+1` => `x == c+1` + {token.GTR, token.LEQ, 1, 1}, + // `x >= c && x <= c` => `x == c` + {token.GEQ, token.LEQ, 0, 0}, + } + for _, comb := range combTable { + if match(&comb) { + lhs.Op = token.EQL + v := c1 + comb.resDelta + lhs.Y.(*ast.BasicLit).Value = fmt.Sprint(v) + cur.Replace(lhs) + return true + } + } + + case token.LOR: + combTable := [...]combination{ + // `x < c || x > c` => `x != c` + {token.LSS, token.GTR, 0, 0}, + // `x <= c || x > c+1` => `x != c+1` + {token.LEQ, token.GTR, 1, 1}, + // `x < c || x >= c+1` => `x != c` + {token.LSS, token.GEQ, 1, 0}, + // `x <= c || x >= c+2` => `x != c+1` + {token.LEQ, token.GEQ, 2, 1}, + } + for _, comb := range combTable { + if match(&comb) { + lhs.Op = token.NEQ + v := c1 + comb.resDelta + lhs.Y.(*ast.BasicLit).Value = fmt.Sprint(v) + cur.Replace(lhs) + return true + } + } + } + + return false +} + +func (c *boolExprSimplifyChecker) int64val(x ast.Expr) (int64, bool) { + // TODO(Quasilyte): if we had types info, we could use TypesInfo.Types[x].Value, + // but since copying erases leaves us without it, only basic literals are handled. + lit, ok := x.(*ast.BasicLit) + if !ok { + return 0, false + } + v, err := strconv.ParseInt(lit.Value, 10, 64) + if err != nil { + return 0, false + } + return v, true +} + func (c *boolExprSimplifyChecker) warn(cause, suggestion ast.Expr) { c.SkipChilds = true c.ctx.Warn(cause, "can simplify `%s` to `%s`", cause, suggestion) diff --git a/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go b/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go index f7928f8ff7d4..24d8b7fff4f3 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go @@ -16,68 +16,17 @@ func init() { info.After = `length := 10` collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { - builtins := map[string]bool{ - // Types - "bool": true, - "byte": true, - "complex64": true, - "complex128": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int8": true, - "int16": true, - "int32": true, - "int64": true, - "rune": true, - "string": true, - "uint": true, - "uint8": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uintptr": true, - - // Constants - "true": true, - "false": true, - "iota": true, - - // Zero value - "nil": true, - - // Functions - "append": true, - "cap": true, - "close": true, - "complex": true, - "copy": true, - "delete": true, - "imag": true, - "len": true, - "make": true, - "new": true, - "panic": true, - "print": true, - "println": true, - "real": true, - "recover": true, - } - c := &builtinShadowChecker{ctx: ctx, builtins: builtins} - return astwalk.WalkerForLocalDef(c, ctx.TypesInfo) + return astwalk.WalkerForLocalDef(&builtinShadowChecker{ctx: ctx}, ctx.TypesInfo) }) } type builtinShadowChecker struct { astwalk.WalkHandler ctx *lintpack.CheckerContext - - builtins map[string]bool } func (c *builtinShadowChecker) VisitLocalDef(name astwalk.Name, _ ast.Expr) { - if _, isBuiltin := c.builtins[name.ID.String()]; isBuiltin { + if isBuiltin(name.ID.Name) { c.warn(name.ID) } } diff --git a/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go b/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go new file mode 100644 index 000000000000..14d89da374b3 --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go @@ -0,0 +1,61 @@ +package checkers + +import ( + "go/ast" + "regexp" + "strings" + + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "codegenComment" + info.Tags = []string{"diagnostic", "experimental"} + info.Summary = "Detects malformed 'code generated' file comments" + info.Before = `// This file was automatically generated by foogen` + info.After = `// Code generated by foogen. DO NOT EDIT.` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + patterns := []string{ + "this (?:file|code) (?:was|is) auto(?:matically)? generated", + "this (?:file|code) (?:was|is) generated automatically", + "this (?:file|code) (?:was|is) generated by", + "this (?:file|code) (?:was|is) (?:auto(?:matically)? )?generated", + "this (?:file|code) (?:was|is) generated", + "code in this file (?:was|is) auto(?:matically)? generated", + "generated (?:file|code) - do not edit", + // TODO(Quasilyte): more of these. + } + re := regexp.MustCompile("(?i)" + strings.Join(patterns, "|")) + return &codegenCommentChecker{ + ctx: ctx, + badCommentRE: re, + } + }) +} + +type codegenCommentChecker struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext + + badCommentRE *regexp.Regexp +} + +func (c *codegenCommentChecker) WalkFile(f *ast.File) { + if f.Doc == nil { + return + } + + for _, comment := range f.Doc.List { + if c.badCommentRE.MatchString(comment.Text) { + c.warn(comment) + return + } + } +} + +func (c *codegenCommentChecker) warn(cause ast.Node) { + c.ctx.Warn(cause, "comment should match `Code generated .* DO NOT EDIT.` regexp") +} diff --git a/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go b/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go index b5dc17e4d061..ed75015e0950 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go @@ -20,7 +20,13 @@ func init() { info.After = `// This is a comment` collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { - pragmaRE := regexp.MustCompile(`(?m)^//\w+:.*$`) + parts := []string{ + `^//\w+:.*$`, //key: value + `^//nolint$`, //nolint + `^//line /.*:\d+`, //line /path/to/file:123 + } + pat := "(?m)" + strings.Join(parts, "|") + pragmaRE := regexp.MustCompile(pat) return astwalk.WalkerForComment(&commentFormattingChecker{ ctx: ctx, pragmaRE: pragmaRE, diff --git a/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go b/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go index f8b3e5625c9a..d68e32fa8e7d 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go @@ -27,6 +27,8 @@ func FuncOld() int` c.commonPatterns = []*regexp.Regexp{ regexp.MustCompile(`(?i)this (?:function|type) is deprecated`), regexp.MustCompile(`(?i)deprecated[.!]? use \S* instead`), + regexp.MustCompile(`(?i)\[\[deprecated\]\].*`), + regexp.MustCompile(`(?i)note: deprecated\b.*`), // TODO(quasilyte): more of these? } @@ -46,6 +48,7 @@ func FuncOld() int` "Deprecate: ", "Derpecate: ", "Derpecated: ", + "Depreacted: ", } for i := range c.commonTypos { c.commonTypos[i] = strings.ToUpper(c.commonTypos[i]) diff --git a/vendor/github.com/go-critic/go-critic/checkers/dupArg_checker.go b/vendor/github.com/go-critic/go-critic/checkers/dupArg_checker.go index 3eb885aa4ed3..431522d8b780 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/dupArg_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/dupArg_checker.go @@ -48,6 +48,9 @@ func init() { c.matchers = map[string]func(*ast.CallExpr) bool{ "copy": m["(x, x, ...)"], + "math.Max": m["(x, x, ...)"], + "math.Min": m["(x, x, ...)"], + "reflect.Copy": m["(x, x, ...)"], "reflect.DeepEqual": m["(x, x, ...)"], diff --git a/vendor/github.com/go-critic/go-critic/checkers/equalFold_checker.go b/vendor/github.com/go-critic/go-critic/checkers/equalFold_checker.go index 3f4fb914bb6e..265b2f79beee 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/equalFold_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/equalFold_checker.go @@ -7,6 +7,7 @@ import ( "github.com/go-lintpack/lintpack" "github.com/go-lintpack/lintpack/astwalk" "github.com/go-toolsmith/astcast" + "github.com/go-toolsmith/astequal" ) func init() { @@ -57,7 +58,9 @@ func (c *equalFoldChecker) checkBytes(expr *ast.CallExpr) { if !ok1 && !ok2 { return } - c.warnBytes(expr, x, y) + if !astequal.Expr(x, y) { + c.warnBytes(expr, x, y) + } } func (c *equalFoldChecker) checkStrings(expr *ast.BinaryExpr) { @@ -70,7 +73,9 @@ func (c *equalFoldChecker) checkStrings(expr *ast.BinaryExpr) { if !ok1 && !ok2 { return } - c.warnStrings(expr, x, y) + if !astequal.Expr(x, y) { + c.warnStrings(expr, x, y) + } } func (c *equalFoldChecker) warnStrings(cause ast.Node, x, y ast.Expr) { diff --git a/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go b/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go index f8d8fc677e46..1d43ba52113f 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go @@ -3,6 +3,7 @@ package checkers import ( "go/ast" "go/constant" + "go/types" "strings" "github.com/go-lintpack/lintpack" @@ -30,13 +31,14 @@ type flagNameChecker struct { func (c *flagNameChecker) VisitExpr(expr ast.Expr) { call := astcast.ToCallExpr(expr) - sym := astcast.ToIdent(astcast.ToSelectorExpr(call.Fun).Sel) - obj := c.ctx.TypesInfo.ObjectOf(sym) - if obj == nil { + calledExpr := astcast.ToSelectorExpr(call.Fun) + obj, ok := c.ctx.TypesInfo.ObjectOf(astcast.ToIdent(calledExpr.X)).(*types.PkgName) + if !ok { return } - pkg := obj.Pkg() - if !isStdlibPkg(pkg) || pkg.Name() != "flag" { + sym := calledExpr.Sel + pkg := obj.Imported() + if pkg.Path() != "flag" { return } diff --git a/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go b/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go new file mode 100644 index 000000000000..a700314cf3db --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go @@ -0,0 +1,60 @@ +package checkers + +import ( + "go/ast" + "go/token" + "strings" + + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "hexLiteral" + info.Tags = []string{"style", "experimental"} + info.Summary = "Detects hex literals that have mixed case letter digits" + info.Before = ` +x := 0X12 +y := 0xfF` + info.After = ` +x := 0x12 +// (A) +y := 0xff +// (B) +y := 0xFF` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + return astwalk.WalkerForExpr(&hexLiteralChecker{ctx: ctx}) + }) +} + +type hexLiteralChecker struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext +} + +func (c *hexLiteralChecker) warn0X(lit *ast.BasicLit) { + suggest := "0x" + lit.Value[len("0X"):] + c.ctx.Warn(lit, "prefer 0x over 0X, s/%s/%s/", lit.Value, suggest) +} + +func (c *hexLiteralChecker) warnMixedDigits(lit *ast.BasicLit) { + c.ctx.Warn(lit, "don't mix hex literal letter digits casing") +} + +func (c *hexLiteralChecker) VisitExpr(expr ast.Expr) { + lit := astcast.ToBasicLit(expr) + if lit.Kind != token.INT || len(lit.Value) < 3 { + return + } + if strings.HasPrefix(lit.Value, "0X") { + c.warn0X(lit) + return + } + digits := lit.Value[len("0x"):] + if strings.ToLower(digits) != digits && strings.ToUpper(digits) != digits { + c.warnMixedDigits(lit) + } +} diff --git a/vendor/github.com/go-critic/go-critic/checkers/internal/lintutil/coerce.go b/vendor/github.com/go-critic/go-critic/checkers/internal/lintutil/coerce.go deleted file mode 100644 index 8a3e35684a32..000000000000 --- a/vendor/github.com/go-critic/go-critic/checkers/internal/lintutil/coerce.go +++ /dev/null @@ -1,121 +0,0 @@ -package lintutil - -import ( - "go/ast" - "go/token" -) - -var ( - nilIdent = &ast.Ident{} - nilSelectorExpr = &ast.SelectorExpr{} - nilUnaryExpr = &ast.UnaryExpr{} - nilBinaryExpr = &ast.BinaryExpr{} - nilCallExpr = &ast.CallExpr{} - nilParenExpr = &ast.ParenExpr{} - nilAssignStmt = &ast.AssignStmt{} -) - -// IsNil reports whether x is nil. -// Unlike simple nil check, also detects nil AST sentinels. -func IsNil(x ast.Node) bool { - switch x := x.(type) { - case *ast.Ident: - return x == nilIdent || x == nil - case *ast.SelectorExpr: - return x == nilSelectorExpr || x == nil - case *ast.UnaryExpr: - return x == nilUnaryExpr || x == nil - case *ast.BinaryExpr: - return x == nilBinaryExpr || x == nil - case *ast.CallExpr: - return x == nilCallExpr || x == nil - case *ast.ParenExpr: - return x == nilParenExpr || x == nil - case *ast.AssignStmt: - return x == nilAssignStmt || x == nil - - default: - return x == nil - } -} - -// AsIdent coerces x into non-nil ident. -func AsIdent(x ast.Node) *ast.Ident { - e, ok := x.(*ast.Ident) - if !ok { - return nilIdent - } - return e -} - -// AsSelectorExpr coerces x into non-nil selector expr. -func AsSelectorExpr(x ast.Node) *ast.SelectorExpr { - e, ok := x.(*ast.SelectorExpr) - if !ok { - return nilSelectorExpr - } - return e -} - -// AsUnaryExpr coerces x into non-nil unary expr. -func AsUnaryExpr(x ast.Node) *ast.UnaryExpr { - e, ok := x.(*ast.UnaryExpr) - if !ok { - return nilUnaryExpr - } - return e -} - -// AsUnaryExprOp is like AsUnaryExpr, but also checks for op token. -func AsUnaryExprOp(x ast.Node, op token.Token) *ast.UnaryExpr { - e, ok := x.(*ast.UnaryExpr) - if !ok || e.Op != op { - return nilUnaryExpr - } - return e -} - -// AsBinaryExpr coerces x into non-nil binary expr. -func AsBinaryExpr(x ast.Node) *ast.BinaryExpr { - e, ok := x.(*ast.BinaryExpr) - if !ok { - return nilBinaryExpr - } - return e -} - -// AsBinaryExprOp is like AsBinaryExpr, but also checks for op token. -func AsBinaryExprOp(x ast.Node, op token.Token) *ast.BinaryExpr { - e, ok := x.(*ast.BinaryExpr) - if !ok || e.Op != op { - return nilBinaryExpr - } - return e -} - -// AsCallExpr coerces x into non-nil call expr. -func AsCallExpr(x ast.Node) *ast.CallExpr { - e, ok := x.(*ast.CallExpr) - if !ok { - return nilCallExpr - } - return e -} - -// AsParenExpr coerces x into non-nil paren expr. -func AsParenExpr(x ast.Node) *ast.ParenExpr { - e, ok := x.(*ast.ParenExpr) - if !ok { - return nilParenExpr - } - return e -} - -// AsAssignStmt coerces x into non-nil assign stmt. -func AsAssignStmt(x ast.Node) *ast.AssignStmt { - stmt, ok := x.(*ast.AssignStmt) - if !ok { - return nilAssignStmt - } - return stmt -} diff --git a/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go b/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go new file mode 100644 index 000000000000..e40ec6db5e35 --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/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/vendor/github.com/go-critic/go-critic/checkers/stringXbytes_checker.go b/vendor/github.com/go-critic/go-critic/checkers/stringXbytes_checker.go new file mode 100644 index 000000000000..74570108e698 --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/checkers/stringXbytes_checker.go @@ -0,0 +1,47 @@ +package checkers + +import ( + "go/ast" + + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/typep" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "stringXbytes" + info.Tags = []string{"style", "experimental"} + info.Summary = "Detects redundant conversions between string and []byte" + info.Before = `copy(b, []byte(s))` + info.After = `copy(b, s)` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + return astwalk.WalkerForExpr(&stringXbytes{ctx: ctx}) + }) +} + +type stringXbytes struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext +} + +func (c *stringXbytes) VisitExpr(expr ast.Expr) { + x, ok := expr.(*ast.CallExpr) + if !ok || qualifiedName(x.Fun) != "copy" { + return + } + + src := x.Args[1] + + byteCast, ok := src.(*ast.CallExpr) + if ok && typep.IsTypeExpr(c.ctx.TypesInfo, byteCast.Fun) && + typep.HasStringProp(c.ctx.TypesInfo.TypeOf(byteCast.Args[0])) { + + c.warn(byteCast, byteCast.Args[0]) + } +} + +func (c *stringXbytes) warn(cause *ast.CallExpr, suggestion ast.Expr) { + c.ctx.Warn(cause, "can simplify `%s` to `%s`", cause, suggestion) +} diff --git a/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go b/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go new file mode 100644 index 000000000000..c0c42e3511c1 --- /dev/null +++ b/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go @@ -0,0 +1,132 @@ +package checkers + +import ( + "go/ast" + "go/token" + + "github.com/go-critic/go-critic/checkers/internal/lintutil" + "github.com/go-lintpack/lintpack" + "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" + "github.com/go-toolsmith/astequal" + "github.com/go-toolsmith/astp" +) + +func init() { + var info lintpack.CheckerInfo + info.Name = "typeAssertChain" + info.Tags = []string{"style", "experimental"} + info.Summary = "Detects repeated type assertions and suggests to replace them with type switch statement" + info.Before = ` +if x, ok := v.(T1); ok { + // Code A, uses x. +} else if x, ok := v.(T2); ok { + // Code B, uses x. +} else if x, ok := v.(T3); ok { + // Code C, uses x. +}` + info.After = ` +switch x := v.(T1) { +case cond1: + // Code A, uses x. +case cond2: + // Code B, uses x. +default: + // Code C, uses x. +}` + + collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { + return astwalk.WalkerForStmt(&typeAssertChainChecker{ctx: ctx}) + }) +} + +type typeAssertChainChecker struct { + astwalk.WalkHandler + ctx *lintpack.CheckerContext + + cause *ast.IfStmt + visited map[*ast.IfStmt]bool + typeSet lintutil.AstSet +} + +func (c *typeAssertChainChecker) EnterFunc(fn *ast.FuncDecl) bool { + if fn.Body == nil { + return false + } + c.visited = make(map[*ast.IfStmt]bool) + return true +} + +func (c *typeAssertChainChecker) VisitStmt(stmt ast.Stmt) { + ifstmt, ok := stmt.(*ast.IfStmt) + if !ok || c.visited[ifstmt] || ifstmt.Init == nil { + return + } + assertion := c.getTypeAssert(ifstmt) + if assertion == nil { + return + } + c.cause = ifstmt + c.checkIfStmt(ifstmt, assertion) +} + +func (c *typeAssertChainChecker) getTypeAssert(ifstmt *ast.IfStmt) *ast.TypeAssertExpr { + assign := astcast.ToAssignStmt(ifstmt.Init) + if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 { + return nil + } + if !astp.IsIdent(assign.Lhs[0]) || assign.Tok != token.DEFINE { + return nil + } + if !astequal.Expr(assign.Lhs[1], ifstmt.Cond) { + return nil + } + + assertion, ok := assign.Rhs[0].(*ast.TypeAssertExpr) + if !ok { + return nil + } + return assertion +} + +func (c *typeAssertChainChecker) checkIfStmt(stmt *ast.IfStmt, assertion *ast.TypeAssertExpr) { + if c.countTypeAssertions(stmt, assertion) >= 2 { + c.warn() + } +} + +func (c *typeAssertChainChecker) countTypeAssertions(stmt *ast.IfStmt, assertion *ast.TypeAssertExpr) int { + c.typeSet.Clear() + + count := 1 + x := assertion.X + c.typeSet.Insert(assertion.Type) + for { + e, ok := stmt.Else.(*ast.IfStmt) + if !ok { + return count + } + assertion = c.getTypeAssert(e) + if assertion == nil { + return count + } + if !c.typeSet.Insert(assertion.Type) { + // Asserted type is duplicated. + // Type switch does not permit duplicate cases, + // so give up. + return 0 + } + if !astequal.Expr(x, assertion.X) { + // Mixed type asserting chain. + // Can't be easily translated to a type switch. + return 0 + } + stmt = e + count++ + c.visited[e] = true + } +} + +func (c *typeAssertChainChecker) warn() { + c.ctx.Warn(c.cause, "rewrite if-else to type switch statement") +} diff --git a/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go b/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go index 3c248ba8e17b..dfc6077bbf1d 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go @@ -4,9 +4,9 @@ import ( "go/ast" "go/types" - "github.com/go-critic/go-critic/checkers/internal/lintutil" "github.com/go-lintpack/lintpack" "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" "github.com/go-toolsmith/astp" ) @@ -45,7 +45,7 @@ type underefChecker struct { func (c *underefChecker) VisitExpr(expr ast.Expr) { switch n := expr.(type) { case *ast.SelectorExpr: - expr := lintutil.AsParenExpr(n.X) + expr := astcast.ToParenExpr(n.X) if c.skipRecvDeref && c.isPtrRecvMethodCall(n.Sel) { return } @@ -56,7 +56,7 @@ func (c *underefChecker) VisitExpr(expr ast.Expr) { } } case *ast.IndexExpr: - expr := lintutil.AsParenExpr(n.X) + expr := astcast.ToParenExpr(n.X) if expr, ok := expr.X.(*ast.StarExpr); ok { if !c.checkStarExpr(expr) { return diff --git a/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go index e7231ce8b4e8..846bb14d254c 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go @@ -4,9 +4,9 @@ import ( "go/ast" "go/types" - "github.com/go-critic/go-critic/checkers/internal/lintutil" "github.com/go-lintpack/lintpack" "github.com/go-lintpack/lintpack/astwalk" + "github.com/go-toolsmith/astcast" "github.com/go-toolsmith/astequal" ) @@ -39,11 +39,14 @@ func (c *unlambdaChecker) VisitExpr(x ast.Expr) { return } - result := lintutil.AsCallExpr(ret.Results[0]) + result := astcast.ToCallExpr(ret.Results[0]) callable := qualifiedName(result.Fun) if callable == "" { return // Skip tricky cases; only handle simple calls } + if isBuiltin(callable) { + return // See #762 + } fnType := c.ctx.TypesInfo.TypeOf(fn) resultType := c.ctx.TypesInfo.TypeOf(result.Fun) if !types.Identical(fnType, resultType) { diff --git a/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go index 0a575e818f12..09423250ad47 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go @@ -44,7 +44,7 @@ func (c *unnamedResultChecker) VisitFuncDecl(decl *ast.FuncDecl) { switch { case results == nil: return // Function has no results - case len(results.List) > 0 && results.List[0].Names != nil: + case len(results.List) != 0 && results.List[0].Names != nil: return // Skip named results } diff --git a/vendor/github.com/go-critic/go-critic/checkers/utils.go b/vendor/github.com/go-critic/go-critic/checkers/utils.go index b59e9188c017..f25a82ef5215 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/utils.go +++ b/vendor/github.com/go-critic/go-critic/checkers/utils.go @@ -8,6 +8,60 @@ import ( "github.com/go-lintpack/lintpack" ) +var goBuiltins = map[string]bool{ + // Types + "bool": true, + "byte": true, + "complex64": true, + "complex128": true, + "error": true, + "float32": true, + "float64": true, + "int": true, + "int8": true, + "int16": true, + "int32": true, + "int64": true, + "rune": true, + "string": true, + "uint": true, + "uint8": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uintptr": true, + + // Constants + "true": true, + "false": true, + "iota": true, + + // Zero value + "nil": true, + + // Functions + "append": true, + "cap": true, + "close": true, + "complex": true, + "copy": true, + "delete": true, + "imag": true, + "len": true, + "make": true, + "new": true, + "panic": true, + "print": true, + "println": true, + "real": true, + "recover": true, +} + +// isBuiltin reports whether sym belongs to a predefined identifier set. +func isBuiltin(sym string) bool { + return goBuiltins[sym] +} + // isStdlibPkg reports whether pkg is a package from the Go standard library. func isStdlibPkg(pkg *types.Package) bool { return pkg != nil && pkg.Path() == pkg.Name() diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go new file mode 100644 index 000000000000..be671c79b701 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go @@ -0,0 +1,723 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Indexed binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; +// see that file for specification of the format. + +// +build go1.11 + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "go/ast" + "go/constant" + "go/token" + "go/types" + "io" + "math/big" + "reflect" + "sort" +) + +// Current indexed export format version. Increase with each format change. +// 0: Go1.11 encoding +const iexportVersion = 0 + +// IExportData returns the binary export data for pkg. +// If no file set is provided, position info will be missing. +func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + + p := iexporter{ + out: bytes.NewBuffer(nil), + fset: fset, + allPkgs: map[*types.Package]bool{}, + stringIndex: map[string]uint64{}, + declIndex: map[types.Object]uint64{}, + typIndex: map[types.Type]uint64{}, + } + + for i, pt := range predeclared() { + p.typIndex[pt] = uint64(i) + } + if len(p.typIndex) > predeclReserved { + panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) + } + + // Initialize work queue with exported declarations. + scope := pkg.Scope() + for _, name := range scope.Names() { + if ast.IsExported(name) { + p.pushDecl(scope.Lookup(name)) + } + } + + // Loop until no more work. + for !p.declTodo.empty() { + p.doDecl(p.declTodo.popHead()) + } + + // Append indices to data0 section. + dataLen := uint64(p.data0.Len()) + w := p.newWriter() + w.writeIndex(p.declIndex, pkg) + w.flush() + + // Assemble header. + var hdr intWriter + hdr.WriteByte('i') + hdr.uint64(iexportVersion) + hdr.uint64(uint64(p.strings.Len())) + hdr.uint64(dataLen) + + // Flush output. + io.Copy(p.out, &hdr) + io.Copy(p.out, &p.strings) + io.Copy(p.out, &p.data0) + + return p.out.Bytes(), nil +} + +// writeIndex writes out an object index. mainIndex indicates whether +// we're writing out the main index, which is also read by +// non-compiler tools and includes a complete package description +// (i.e., name and height). +func (w *exportWriter) writeIndex(index map[types.Object]uint64, localpkg *types.Package) { + // Build a map from packages to objects from that package. + pkgObjs := map[*types.Package][]types.Object{} + + // For the main index, make sure to include every package that + // we reference, even if we're not exporting (or reexporting) + // any symbols from it. + pkgObjs[localpkg] = nil + for pkg := range w.p.allPkgs { + pkgObjs[pkg] = nil + } + + for obj := range index { + pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], obj) + } + + var pkgs []*types.Package + for pkg, objs := range pkgObjs { + pkgs = append(pkgs, pkg) + + sort.Slice(objs, func(i, j int) bool { + return objs[i].Name() < objs[j].Name() + }) + } + + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i].Path() < pkgs[j].Path() + }) + + w.uint64(uint64(len(pkgs))) + for _, pkg := range pkgs { + w.string(pkg.Path()) + w.string(pkg.Name()) + w.uint64(uint64(0)) // package height is not needed for go/types + + objs := pkgObjs[pkg] + w.uint64(uint64(len(objs))) + for _, obj := range objs { + w.string(obj.Name()) + w.uint64(index[obj]) + } + } +} + +type iexporter struct { + fset *token.FileSet + out *bytes.Buffer + + // allPkgs tracks all packages that have been referenced by + // the export data, so we can ensure to include them in the + // main index. + allPkgs map[*types.Package]bool + + declTodo objQueue + + strings intWriter + stringIndex map[string]uint64 + + data0 intWriter + declIndex map[types.Object]uint64 + typIndex map[types.Type]uint64 +} + +// stringOff returns the offset of s within the string section. +// If not already present, it's added to the end. +func (p *iexporter) stringOff(s string) uint64 { + off, ok := p.stringIndex[s] + if !ok { + off = uint64(p.strings.Len()) + p.stringIndex[s] = off + + p.strings.uint64(uint64(len(s))) + p.strings.WriteString(s) + } + return off +} + +// pushDecl adds n to the declaration work queue, if not already present. +func (p *iexporter) pushDecl(obj types.Object) { + // Package unsafe is known to the compiler and predeclared. + assert(obj.Pkg() != types.Unsafe) + + if _, ok := p.declIndex[obj]; ok { + return + } + + p.declIndex[obj] = ^uint64(0) // mark n present in work queue + p.declTodo.pushTail(obj) +} + +// exportWriter handles writing out individual data section chunks. +type exportWriter struct { + p *iexporter + + data intWriter + currPkg *types.Package + prevFile string + prevLine int64 +} + +func (p *iexporter) doDecl(obj types.Object) { + w := p.newWriter() + w.setPkg(obj.Pkg(), false) + + switch obj := obj.(type) { + case *types.Var: + w.tag('V') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + + case *types.Func: + sig, _ := obj.Type().(*types.Signature) + if sig.Recv() != nil { + panic(internalErrorf("unexpected method: %v", sig)) + } + w.tag('F') + w.pos(obj.Pos()) + w.signature(sig) + + case *types.Const: + w.tag('C') + w.pos(obj.Pos()) + w.value(obj.Type(), obj.Val()) + + case *types.TypeName: + if obj.IsAlias() { + w.tag('A') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + break + } + + // Defined type. + w.tag('T') + w.pos(obj.Pos()) + + underlying := obj.Type().Underlying() + w.typ(underlying, obj.Pkg()) + + t := obj.Type() + if types.IsInterface(t) { + break + } + + named, ok := t.(*types.Named) + if !ok { + panic(internalErrorf("%s is not a defined type", t)) + } + + n := named.NumMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := named.Method(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.param(sig.Recv()) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected object: %v", obj)) + } + + p.declIndex[obj] = w.flush() +} + +func (w *exportWriter) tag(tag byte) { + w.data.WriteByte(tag) +} + +func (w *exportWriter) pos(pos token.Pos) { + p := w.p.fset.Position(pos) + file := p.Filename + line := int64(p.Line) + + // When file is the same as the last position (common case), + // we can save a few bytes by delta encoding just the line + // number. + // + // Note: Because data objects may be read out of order (or not + // at all), we can only apply delta encoding within a single + // object. This is handled implicitly by tracking prevFile and + // prevLine as fields of exportWriter. + + if file == w.prevFile { + delta := line - w.prevLine + w.int64(delta) + if delta == deltaNewFile { + w.int64(-1) + } + } else { + w.int64(deltaNewFile) + w.int64(line) // line >= 0 + w.string(file) + w.prevFile = file + } + w.prevLine = line +} + +func (w *exportWriter) pkg(pkg *types.Package) { + // Ensure any referenced packages are declared in the main index. + w.p.allPkgs[pkg] = true + + w.string(pkg.Path()) +} + +func (w *exportWriter) qualifiedIdent(obj types.Object) { + // Ensure any referenced declarations are written out too. + w.p.pushDecl(obj) + + w.string(obj.Name()) + w.pkg(obj.Pkg()) +} + +func (w *exportWriter) typ(t types.Type, pkg *types.Package) { + w.data.uint64(w.p.typOff(t, pkg)) +} + +func (p *iexporter) newWriter() *exportWriter { + return &exportWriter{p: p} +} + +func (w *exportWriter) flush() uint64 { + off := uint64(w.p.data0.Len()) + io.Copy(&w.p.data0, &w.data) + return off +} + +func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { + off, ok := p.typIndex[t] + if !ok { + w := p.newWriter() + w.doTyp(t, pkg) + off = predeclReserved + w.flush() + p.typIndex[t] = off + } + return off +} + +func (w *exportWriter) startType(k itag) { + w.data.uint64(uint64(k)) +} + +func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { + switch t := t.(type) { + case *types.Named: + w.startType(definedType) + w.qualifiedIdent(t.Obj()) + + case *types.Pointer: + w.startType(pointerType) + w.typ(t.Elem(), pkg) + + case *types.Slice: + w.startType(sliceType) + w.typ(t.Elem(), pkg) + + case *types.Array: + w.startType(arrayType) + w.uint64(uint64(t.Len())) + w.typ(t.Elem(), pkg) + + case *types.Chan: + w.startType(chanType) + // 1 RecvOnly; 2 SendOnly; 3 SendRecv + var dir uint64 + switch t.Dir() { + case types.RecvOnly: + dir = 1 + case types.SendOnly: + dir = 2 + case types.SendRecv: + dir = 3 + } + w.uint64(dir) + w.typ(t.Elem(), pkg) + + case *types.Map: + w.startType(mapType) + w.typ(t.Key(), pkg) + w.typ(t.Elem(), pkg) + + case *types.Signature: + w.startType(signatureType) + w.setPkg(pkg, true) + w.signature(t) + + case *types.Struct: + w.startType(structType) + w.setPkg(pkg, true) + + n := t.NumFields() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Field(i) + w.pos(f.Pos()) + w.string(f.Name()) + w.typ(f.Type(), pkg) + w.bool(f.Embedded()) + w.string(t.Tag(i)) // note (or tag) + } + + case *types.Interface: + w.startType(interfaceType) + w.setPkg(pkg, true) + + n := t.NumEmbeddeds() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Embedded(i) + w.pos(f.Obj().Pos()) + w.typ(f.Obj().Type(), f.Obj().Pkg()) + } + + n = t.NumExplicitMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := t.ExplicitMethod(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) + } +} + +func (w *exportWriter) setPkg(pkg *types.Package, write bool) { + if write { + w.pkg(pkg) + } + + w.currPkg = pkg +} + +func (w *exportWriter) signature(sig *types.Signature) { + w.paramList(sig.Params()) + w.paramList(sig.Results()) + if sig.Params().Len() > 0 { + w.bool(sig.Variadic()) + } +} + +func (w *exportWriter) paramList(tup *types.Tuple) { + n := tup.Len() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + w.param(tup.At(i)) + } +} + +func (w *exportWriter) param(obj types.Object) { + w.pos(obj.Pos()) + w.localIdent(obj) + w.typ(obj.Type(), obj.Pkg()) +} + +func (w *exportWriter) value(typ types.Type, v constant.Value) { + w.typ(typ, nil) + + switch v.Kind() { + case constant.Bool: + w.bool(constant.BoolVal(v)) + case constant.Int: + var i big.Int + if i64, exact := constant.Int64Val(v); exact { + i.SetInt64(i64) + } else if ui64, exact := constant.Uint64Val(v); exact { + i.SetUint64(ui64) + } else { + i.SetString(v.ExactString(), 10) + } + w.mpint(&i, typ) + case constant.Float: + f := constantToFloat(v) + w.mpfloat(f, typ) + case constant.Complex: + w.mpfloat(constantToFloat(constant.Real(v)), typ) + w.mpfloat(constantToFloat(constant.Imag(v)), typ) + case constant.String: + w.string(constant.StringVal(v)) + case constant.Unknown: + // package contains type errors + default: + panic(internalErrorf("unexpected value %v (%T)", v, v)) + } +} + +// constantToFloat converts a constant.Value with kind constant.Float to a +// big.Float. +func constantToFloat(x constant.Value) *big.Float { + assert(x.Kind() == constant.Float) + // Use the same floating-point precision (512) as cmd/compile + // (see Mpprec in cmd/compile/internal/gc/mpfloat.go). + const mpprec = 512 + var f big.Float + f.SetPrec(mpprec) + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + n := valueToRat(num) + d := valueToRat(denom) + f.SetRat(n.Quo(n, d)) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + _, ok := f.SetString(x.ExactString()) + assert(ok) + } + return &f +} + +// mpint exports a multi-precision integer. +// +// For unsigned types, small values are written out as a single +// byte. Larger values are written out as a length-prefixed big-endian +// byte string, where the length prefix is encoded as its complement. +// For example, bytes 0, 1, and 2 directly represent the integer +// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, +// 2-, and 3-byte big-endian string follow. +// +// Encoding for signed types use the same general approach as for +// unsigned types, except small values use zig-zag encoding and the +// bottom bit of length prefix byte for large values is reserved as a +// sign bit. +// +// The exact boundary between small and large encodings varies +// according to the maximum number of bytes needed to encode a value +// of type typ. As a special case, 8-bit types are always encoded as a +// single byte. +// +// TODO(mdempsky): Is this level of complexity really worthwhile? +func (w *exportWriter) mpint(x *big.Int, typ types.Type) { + basic, ok := typ.Underlying().(*types.Basic) + if !ok { + panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) + } + + signed, maxBytes := intSize(basic) + + negative := x.Sign() < 0 + if !signed && negative { + panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) + } + + b := x.Bytes() + if len(b) > 0 && b[0] == 0 { + panic(internalErrorf("leading zeros")) + } + if uint(len(b)) > maxBytes { + panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) + } + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + // Check if x can use small value encoding. + if len(b) <= 1 { + var ux uint + if len(b) == 1 { + ux = uint(b[0]) + } + if signed { + ux <<= 1 + if negative { + ux-- + } + } + if ux < maxSmall { + w.data.WriteByte(byte(ux)) + return + } + } + + n := 256 - uint(len(b)) + if signed { + n = 256 - 2*uint(len(b)) + if negative { + n |= 1 + } + } + if n < maxSmall || n >= 256 { + panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) + } + + w.data.WriteByte(byte(n)) + w.data.Write(b) +} + +// mpfloat exports a multi-precision floating point number. +// +// The number's value is decomposed into mantissa × 2**exponent, where +// mantissa is an integer. The value is written out as mantissa (as a +// multi-precision integer) and then the exponent, except exponent is +// omitted if mantissa is zero. +func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { + if f.IsInf() { + panic("infinite constant") + } + + // Break into f = mant × 2**exp, with 0.5 <= mant < 1. + var mant big.Float + exp := int64(f.MantExp(&mant)) + + // Scale so that mant is an integer. + prec := mant.MinPrec() + mant.SetMantExp(&mant, int(prec)) + exp -= int64(prec) + + manti, acc := mant.Int(nil) + if acc != big.Exact { + panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) + } + w.mpint(manti, typ) + if manti.Sign() != 0 { + w.int64(exp) + } +} + +func (w *exportWriter) bool(b bool) bool { + var x uint64 + if b { + x = 1 + } + w.uint64(x) + return b +} + +func (w *exportWriter) int64(x int64) { w.data.int64(x) } +func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } +func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } + +func (w *exportWriter) localIdent(obj types.Object) { + // Anonymous parameters. + if obj == nil { + w.string("") + return + } + + name := obj.Name() + if name == "_" { + w.string("_") + return + } + + w.string(name) +} + +type intWriter struct { + bytes.Buffer +} + +func (w *intWriter) int64(x int64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], x) + w.Write(buf[:n]) +} + +func (w *intWriter) uint64(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + w.Write(buf[:n]) +} + +func assert(cond bool) { + if !cond { + panic("internal error: assertion failed") + } +} + +// The below is copied from go/src/cmd/compile/internal/gc/syntax.go. + +// objQueue is a FIFO queue of types.Object. The zero value of objQueue is +// a ready-to-use empty queue. +type objQueue struct { + ring []types.Object + head, tail int +} + +// empty returns true if q contains no Nodes. +func (q *objQueue) empty() bool { + return q.head == q.tail +} + +// pushTail appends n to the tail of the queue. +func (q *objQueue) pushTail(obj types.Object) { + if len(q.ring) == 0 { + q.ring = make([]types.Object, 16) + } else if q.head+len(q.ring) == q.tail { + // Grow the ring. + nring := make([]types.Object, len(q.ring)*2) + // Copy the old elements. + part := q.ring[q.head%len(q.ring):] + if q.tail-q.head <= len(part) { + part = part[:q.tail-q.head] + copy(nring, part) + } else { + pos := copy(nring, part) + copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) + } + q.ring, q.head, q.tail = nring, 0, q.tail-q.head + } + + q.ring[q.tail%len(q.ring)] = obj + q.tail++ +} + +// popHead pops a node from the head of the queue. It panics if q is empty. +func (q *objQueue) popHead() types.Object { + if q.empty() { + panic("dequeue empty") + } + obj := q.ring[q.head%len(q.ring)] + q.head++ + return obj +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 24d145cb29f4..4347427c105b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,7 +10,7 @@ github.com/davecgh/go-spew/spew github.com/fatih/color # github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify -# github.com/go-critic/go-critic v0.0.0-20181204210945-0af0999fabfb +# github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead github.com/go-critic/go-critic/checkers github.com/go-critic/go-critic/checkers/internal/lintutil # github.com/go-lintpack/lintpack v0.5.2