diff options
Diffstat (limited to 'vendor/honnef.co/go/tools/lint/lint.go')
| -rw-r--r-- | vendor/honnef.co/go/tools/lint/lint.go | 844 |
1 files changed, 0 insertions, 844 deletions
diff --git a/vendor/honnef.co/go/tools/lint/lint.go b/vendor/honnef.co/go/tools/lint/lint.go deleted file mode 100644 index 75a5198..0000000 --- a/vendor/honnef.co/go/tools/lint/lint.go +++ /dev/null @@ -1,844 +0,0 @@ -// Copyright (c) 2013 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 or at -// https://developers.google.com/open-source/licenses/bsd. - -// Package lint provides the foundation for tools like gosimple. -package lint // import "honnef.co/go/tools/lint" - -import ( - "bytes" - "fmt" - "go/ast" - "go/build" - "go/constant" - "go/printer" - "go/token" - "go/types" - "path/filepath" - "runtime" - "sort" - "strings" - "sync" - "unicode" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/loader" - "honnef.co/go/tools/ssa" - "honnef.co/go/tools/ssa/ssautil" -) - -type Job struct { - Program *Program - - checker string - check string - problems []Problem -} - -type Ignore interface { - Match(p Problem) bool -} - -type LineIgnore struct { - File string - Line int - Checks []string - matched bool - pos token.Pos -} - -func (li *LineIgnore) Match(p Problem) bool { - if p.Position.Filename != li.File || p.Position.Line != li.Line { - return false - } - for _, c := range li.Checks { - if m, _ := filepath.Match(c, p.Check); m { - li.matched = true - return true - } - } - return false -} - -func (li *LineIgnore) String() string { - matched := "not matched" - if li.matched { - matched = "matched" - } - return fmt.Sprintf("%s:%d %s (%s)", li.File, li.Line, strings.Join(li.Checks, ", "), matched) -} - -type FileIgnore struct { - File string - Checks []string -} - -func (fi *FileIgnore) Match(p Problem) bool { - if p.Position.Filename != fi.File { - return false - } - for _, c := range fi.Checks { - if m, _ := filepath.Match(c, p.Check); m { - return true - } - } - return false -} - -type GlobIgnore struct { - Pattern string - Checks []string -} - -func (gi *GlobIgnore) Match(p Problem) bool { - if gi.Pattern != "*" { - pkgpath := p.Package.Path() - if strings.HasSuffix(pkgpath, "_test") { - pkgpath = pkgpath[:len(pkgpath)-len("_test")] - } - name := filepath.Join(pkgpath, filepath.Base(p.Position.Filename)) - if m, _ := filepath.Match(gi.Pattern, name); !m { - return false - } - } - for _, c := range gi.Checks { - if m, _ := filepath.Match(c, p.Check); m { - return true - } - } - return false -} - -type Program struct { - SSA *ssa.Program - Prog *loader.Program - // TODO(dh): Rename to InitialPackages? - Packages []*Pkg - InitialFunctions []*ssa.Function - AllFunctions []*ssa.Function - Files []*ast.File - Info *types.Info - GoVersion int - - tokenFileMap map[*token.File]*ast.File - astFileMap map[*ast.File]*Pkg -} - -type Func func(*Job) - -// Problem represents a problem in some source code. -type Problem struct { - pos token.Pos - Position token.Position // position in source file - Text string // the prose that describes the problem - Check string - Checker string - Package *types.Package - Ignored bool -} - -func (p *Problem) String() string { - if p.Check == "" { - return p.Text - } - return fmt.Sprintf("%s (%s)", p.Text, p.Check) -} - -type Checker interface { - Name() string - Prefix() string - Init(*Program) - Funcs() map[string]Func -} - -// A Linter lints Go source code. -type Linter struct { - Checker Checker - Ignores []Ignore - GoVersion int - ReturnIgnored bool - - automaticIgnores []Ignore -} - -func (l *Linter) ignore(p Problem) bool { - ignored := false - for _, ig := range l.automaticIgnores { - // We cannot short-circuit these, as we want to record, for - // each ignore, whether it matched or not. - if ig.Match(p) { - ignored = true - } - } - if ignored { - // no need to execute other ignores if we've already had a - // match. - return true - } - for _, ig := range l.Ignores { - // We can short-circuit here, as we aren't tracking any - // information. - if ig.Match(p) { - return true - } - } - - return false -} - -func (prog *Program) File(node Positioner) *ast.File { - return prog.tokenFileMap[prog.SSA.Fset.File(node.Pos())] -} - -func (j *Job) File(node Positioner) *ast.File { - return j.Program.File(node) -} - -// TODO(dh): switch to sort.Slice when Go 1.9 lands. -type byPosition struct { - fset *token.FileSet - ps []Problem -} - -func (ps byPosition) Len() int { - return len(ps.ps) -} - -func (ps byPosition) Less(i int, j int) bool { - pi, pj := ps.ps[i].Position, ps.ps[j].Position - - if pi.Filename != pj.Filename { - return pi.Filename < pj.Filename - } - if pi.Line != pj.Line { - return pi.Line < pj.Line - } - if pi.Column != pj.Column { - return pi.Column < pj.Column - } - - return ps.ps[i].Text < ps.ps[j].Text -} - -func (ps byPosition) Swap(i int, j int) { - ps.ps[i], ps.ps[j] = ps.ps[j], ps.ps[i] -} - -func parseDirective(s string) (cmd string, args []string) { - if !strings.HasPrefix(s, "//lint:") { - return "", nil - } - s = strings.TrimPrefix(s, "//lint:") - fields := strings.Split(s, " ") - return fields[0], fields[1:] -} - -func (l *Linter) Lint(lprog *loader.Program, conf *loader.Config) []Problem { - ssaprog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) - ssaprog.Build() - pkgMap := map[*ssa.Package]*Pkg{} - var pkgs []*Pkg - for _, pkginfo := range lprog.InitialPackages() { - ssapkg := ssaprog.Package(pkginfo.Pkg) - var bp *build.Package - if len(pkginfo.Files) != 0 { - path := lprog.Fset.Position(pkginfo.Files[0].Pos()).Filename - dir := filepath.Dir(path) - var err error - ctx := conf.Build - if ctx == nil { - ctx = &build.Default - } - bp, err = ctx.ImportDir(dir, 0) - if err != nil { - // shouldn't happen - } - } - pkg := &Pkg{ - Package: ssapkg, - Info: pkginfo, - BuildPkg: bp, - } - pkgMap[ssapkg] = pkg - pkgs = append(pkgs, pkg) - } - prog := &Program{ - SSA: ssaprog, - Prog: lprog, - Packages: pkgs, - Info: &types.Info{}, - GoVersion: l.GoVersion, - tokenFileMap: map[*token.File]*ast.File{}, - astFileMap: map[*ast.File]*Pkg{}, - } - - initial := map[*types.Package]struct{}{} - for _, pkg := range pkgs { - initial[pkg.Info.Pkg] = struct{}{} - } - for fn := range ssautil.AllFunctions(ssaprog) { - if fn.Pkg == nil { - continue - } - prog.AllFunctions = append(prog.AllFunctions, fn) - if _, ok := initial[fn.Pkg.Pkg]; ok { - prog.InitialFunctions = append(prog.InitialFunctions, fn) - } - } - for _, pkg := range pkgs { - prog.Files = append(prog.Files, pkg.Info.Files...) - - ssapkg := ssaprog.Package(pkg.Info.Pkg) - for _, f := range pkg.Info.Files { - prog.astFileMap[f] = pkgMap[ssapkg] - } - } - - for _, pkginfo := range lprog.AllPackages { - for _, f := range pkginfo.Files { - tf := lprog.Fset.File(f.Pos()) - prog.tokenFileMap[tf] = f - } - } - - var out []Problem - l.automaticIgnores = nil - for _, pkginfo := range lprog.InitialPackages() { - for _, f := range pkginfo.Files { - cm := ast.NewCommentMap(lprog.Fset, f, f.Comments) - for node, cgs := range cm { - for _, cg := range cgs { - for _, c := range cg.List { - if !strings.HasPrefix(c.Text, "//lint:") { - continue - } - cmd, args := parseDirective(c.Text) - switch cmd { - case "ignore", "file-ignore": - if len(args) < 2 { - // FIXME(dh): this causes duplicated warnings when using megacheck - p := Problem{ - pos: c.Pos(), - Position: prog.DisplayPosition(c.Pos()), - Text: "malformed linter directive; missing the required reason field?", - Check: "", - Checker: l.Checker.Name(), - Package: nil, - } - out = append(out, p) - continue - } - default: - // unknown directive, ignore - continue - } - checks := strings.Split(args[0], ",") - pos := prog.DisplayPosition(node.Pos()) - var ig Ignore - switch cmd { - case "ignore": - ig = &LineIgnore{ - File: pos.Filename, - Line: pos.Line, - Checks: checks, - pos: c.Pos(), - } - case "file-ignore": - ig = &FileIgnore{ - File: pos.Filename, - Checks: checks, - } - } - l.automaticIgnores = append(l.automaticIgnores, ig) - } - } - } - } - } - - sizes := struct { - types int - defs int - uses int - implicits int - selections int - scopes int - }{} - for _, pkg := range pkgs { - sizes.types += len(pkg.Info.Info.Types) - sizes.defs += len(pkg.Info.Info.Defs) - sizes.uses += len(pkg.Info.Info.Uses) - sizes.implicits += len(pkg.Info.Info.Implicits) - sizes.selections += len(pkg.Info.Info.Selections) - sizes.scopes += len(pkg.Info.Info.Scopes) - } - prog.Info.Types = make(map[ast.Expr]types.TypeAndValue, sizes.types) - prog.Info.Defs = make(map[*ast.Ident]types.Object, sizes.defs) - prog.Info.Uses = make(map[*ast.Ident]types.Object, sizes.uses) - prog.Info.Implicits = make(map[ast.Node]types.Object, sizes.implicits) - prog.Info.Selections = make(map[*ast.SelectorExpr]*types.Selection, sizes.selections) - prog.Info.Scopes = make(map[ast.Node]*types.Scope, sizes.scopes) - for _, pkg := range pkgs { - for k, v := range pkg.Info.Info.Types { - prog.Info.Types[k] = v - } - for k, v := range pkg.Info.Info.Defs { - prog.Info.Defs[k] = v - } - for k, v := range pkg.Info.Info.Uses { - prog.Info.Uses[k] = v - } - for k, v := range pkg.Info.Info.Implicits { - prog.Info.Implicits[k] = v - } - for k, v := range pkg.Info.Info.Selections { - prog.Info.Selections[k] = v - } - for k, v := range pkg.Info.Info.Scopes { - prog.Info.Scopes[k] = v - } - } - l.Checker.Init(prog) - - funcs := l.Checker.Funcs() - var keys []string - for k := range funcs { - keys = append(keys, k) - } - sort.Strings(keys) - - var jobs []*Job - for _, k := range keys { - j := &Job{ - Program: prog, - checker: l.Checker.Name(), - check: k, - } - jobs = append(jobs, j) - } - wg := &sync.WaitGroup{} - for _, j := range jobs { - wg.Add(1) - go func(j *Job) { - defer wg.Done() - fn := funcs[j.check] - if fn == nil { - return - } - fn(j) - }(j) - } - wg.Wait() - - for _, j := range jobs { - for _, p := range j.problems { - p.Ignored = l.ignore(p) - if l.ReturnIgnored || !p.Ignored { - out = append(out, p) - } - } - } - - for _, ig := range l.automaticIgnores { - ig, ok := ig.(*LineIgnore) - if !ok { - continue - } - if ig.matched { - continue - } - for _, c := range ig.Checks { - idx := strings.IndexFunc(c, func(r rune) bool { - return unicode.IsNumber(r) - }) - if idx == -1 { - // malformed check name, backing out - continue - } - if c[:idx] != l.Checker.Prefix() { - // not for this checker - continue - } - p := Problem{ - pos: ig.pos, - Position: prog.DisplayPosition(ig.pos), - Text: "this linter directive didn't match anything; should it be removed?", - Check: "", - Checker: l.Checker.Name(), - Package: nil, - } - out = append(out, p) - } - } - - sort.Sort(byPosition{lprog.Fset, out}) - return out -} - -// Pkg represents a package being linted. -type Pkg struct { - *ssa.Package - Info *loader.PackageInfo - BuildPkg *build.Package -} - -type packager interface { - Package() *ssa.Package -} - -func IsExample(fn *ssa.Function) bool { - if !strings.HasPrefix(fn.Name(), "Example") { - return false - } - f := fn.Prog.Fset.File(fn.Pos()) - if f == nil { - return false - } - return strings.HasSuffix(f.Name(), "_test.go") -} - -func (j *Job) IsInTest(node Positioner) bool { - f := j.Program.SSA.Fset.File(node.Pos()) - return f != nil && strings.HasSuffix(f.Name(), "_test.go") -} - -func (j *Job) IsInMain(node Positioner) bool { - if node, ok := node.(packager); ok { - return node.Package().Pkg.Name() == "main" - } - pkg := j.NodePackage(node) - if pkg == nil { - return false - } - return pkg.Pkg.Name() == "main" -} - -type Positioner interface { - Pos() token.Pos -} - -func (prog *Program) DisplayPosition(p token.Pos) token.Position { - // The //line compiler directive can be used to change the file - // name and line numbers associated with code. This can, for - // example, be used by code generation tools. The most prominent - // example is 'go tool cgo', which uses //line directives to refer - // back to the original source code. - // - // In the context of our linters, we need to treat these - // directives differently depending on context. For cgo files, we - // want to honour the directives, so that line numbers are - // adjusted correctly. For all other files, we want to ignore the - // directives, so that problems are reported at their actual - // position and not, for example, a yacc grammar file. This also - // affects the ignore mechanism, since it operates on the position - // information stored within problems. With this implementation, a - // user will ignore foo.go, not foo.y - - pkg := prog.astFileMap[prog.tokenFileMap[prog.Prog.Fset.File(p)]] - bp := pkg.BuildPkg - adjPos := prog.Prog.Fset.Position(p) - if bp == nil { - // couldn't find the package for some reason (deleted? faulty - // file system?) - return adjPos - } - base := filepath.Base(adjPos.Filename) - for _, f := range bp.CgoFiles { - if f == base { - // this is a cgo file, use the adjusted position - return adjPos - } - } - // not a cgo file, ignore //line directives - return prog.Prog.Fset.PositionFor(p, false) -} - -func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem { - tf := j.Program.SSA.Fset.File(n.Pos()) - f := j.Program.tokenFileMap[tf] - pkg := j.Program.astFileMap[f].Pkg - - pos := j.Program.DisplayPosition(n.Pos()) - problem := Problem{ - pos: n.Pos(), - Position: pos, - Text: fmt.Sprintf(format, args...), - Check: j.check, - Checker: j.checker, - Package: pkg, - } - j.problems = append(j.problems, problem) - return &j.problems[len(j.problems)-1] -} - -func (j *Job) Render(x interface{}) string { - fset := j.Program.SSA.Fset - var buf bytes.Buffer - if err := printer.Fprint(&buf, fset, x); err != nil { - panic(err) - } - return buf.String() -} - -func (j *Job) RenderArgs(args []ast.Expr) string { - var ss []string - for _, arg := range args { - ss = append(ss, j.Render(arg)) - } - return strings.Join(ss, ", ") -} - -func IsIdent(expr ast.Expr, ident string) bool { - id, ok := expr.(*ast.Ident) - return ok && id.Name == ident -} - -// isBlank returns whether id is the blank identifier "_". -// If id == nil, the answer is false. -func IsBlank(id ast.Expr) bool { - ident, ok := id.(*ast.Ident) - return ok && ident.Name == "_" -} - -func IsZero(expr ast.Expr) bool { - lit, ok := expr.(*ast.BasicLit) - return ok && lit.Kind == token.INT && lit.Value == "0" -} - -func (j *Job) IsNil(expr ast.Expr) bool { - return j.Program.Info.Types[expr].IsNil() -} - -func (j *Job) BoolConst(expr ast.Expr) bool { - val := j.Program.Info.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val() - return constant.BoolVal(val) -} - -func (j *Job) IsBoolConst(expr ast.Expr) bool { - // We explicitly don't support typed bools because more often than - // not, custom bool types are used as binary enums and the - // explicit comparison is desired. - - ident, ok := expr.(*ast.Ident) - if !ok { - return false - } - obj := j.Program.Info.ObjectOf(ident) - c, ok := obj.(*types.Const) - if !ok { - return false - } - basic, ok := c.Type().(*types.Basic) - if !ok { - return false - } - if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool { - return false - } - return true -} - -func (j *Job) ExprToInt(expr ast.Expr) (int64, bool) { - tv := j.Program.Info.Types[expr] - if tv.Value == nil { - return 0, false - } - if tv.Value.Kind() != constant.Int { - return 0, false - } - return constant.Int64Val(tv.Value) -} - -func (j *Job) ExprToString(expr ast.Expr) (string, bool) { - val := j.Program.Info.Types[expr].Value - if val == nil { - return "", false - } - if val.Kind() != constant.String { - return "", false - } - return constant.StringVal(val), true -} - -func (j *Job) NodePackage(node Positioner) *Pkg { - f := j.File(node) - return j.Program.astFileMap[f] -} - -func IsGenerated(f *ast.File) bool { - comments := f.Comments - if len(comments) > 0 { - comment := comments[0].Text() - return strings.Contains(comment, "Code generated by") || - strings.Contains(comment, "DO NOT EDIT") - } - return false -} - -func Preamble(f *ast.File) string { - cutoff := f.Package - if f.Doc != nil { - cutoff = f.Doc.Pos() - } - var out []string - for _, cmt := range f.Comments { - if cmt.Pos() >= cutoff { - break - } - out = append(out, cmt.Text()) - } - return strings.Join(out, "\n") -} - -func IsPointerLike(T types.Type) bool { - switch T := T.Underlying().(type) { - case *types.Interface, *types.Chan, *types.Map, *types.Pointer: - return true - case *types.Basic: - return T.Kind() == types.UnsafePointer - } - return false -} - -func (j *Job) IsGoVersion(minor int) bool { - return j.Program.GoVersion >= minor -} - -func (j *Job) IsCallToAST(node ast.Node, name string) bool { - call, ok := node.(*ast.CallExpr) - if !ok { - return false - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - fn, ok := j.Program.Info.ObjectOf(sel.Sel).(*types.Func) - return ok && fn.FullName() == name -} - -func (j *Job) IsCallToAnyAST(node ast.Node, names ...string) bool { - for _, name := range names { - if j.IsCallToAST(node, name) { - return true - } - } - return false -} - -func CallName(call *ssa.CallCommon) string { - if call.IsInvoke() { - return "" - } - switch v := call.Value.(type) { - case *ssa.Function: - fn, ok := v.Object().(*types.Func) - if !ok { - return "" - } - return fn.FullName() - case *ssa.Builtin: - return v.Name() - } - return "" -} - -func IsCallTo(call *ssa.CallCommon, name string) bool { - return CallName(call) == name -} - -func FilterDebug(instr []ssa.Instruction) []ssa.Instruction { - var out []ssa.Instruction - for _, ins := range instr { - if _, ok := ins.(*ssa.DebugRef); !ok { - out = append(out, ins) - } - } - return out -} - -func NodeFns(pkgs []*Pkg) map[ast.Node]*ssa.Function { - out := map[ast.Node]*ssa.Function{} - - wg := &sync.WaitGroup{} - chNodeFns := make(chan map[ast.Node]*ssa.Function, runtime.NumCPU()*2) - for _, pkg := range pkgs { - pkg := pkg - wg.Add(1) - go func() { - m := map[ast.Node]*ssa.Function{} - for _, f := range pkg.Info.Files { - ast.Walk(&globalVisitor{m, pkg, f}, f) - } - chNodeFns <- m - wg.Done() - }() - } - go func() { - wg.Wait() - close(chNodeFns) - }() - - for nodeFns := range chNodeFns { - for k, v := range nodeFns { - out[k] = v - } - } - - return out -} - -type globalVisitor struct { - m map[ast.Node]*ssa.Function - pkg *Pkg - f *ast.File -} - -func (v *globalVisitor) Visit(node ast.Node) ast.Visitor { - switch node := node.(type) { - case *ast.CallExpr: - v.m[node] = v.pkg.Func("init") - return v - case *ast.FuncDecl, *ast.FuncLit: - nv := &fnVisitor{v.m, v.f, v.pkg, nil} - return nv.Visit(node) - default: - return v - } -} - -type fnVisitor struct { - m map[ast.Node]*ssa.Function - f *ast.File - pkg *Pkg - ssafn *ssa.Function -} - -func (v *fnVisitor) Visit(node ast.Node) ast.Visitor { - switch node := node.(type) { - case *ast.FuncDecl: - var ssafn *ssa.Function - ssafn = v.pkg.Prog.FuncValue(v.pkg.Info.ObjectOf(node.Name).(*types.Func)) - v.m[node] = ssafn - if ssafn == nil { - return nil - } - return &fnVisitor{v.m, v.f, v.pkg, ssafn} - case *ast.FuncLit: - var ssafn *ssa.Function - path, _ := astutil.PathEnclosingInterval(v.f, node.Pos(), node.Pos()) - ssafn = ssa.EnclosingFunction(v.pkg.Package, path) - v.m[node] = ssafn - if ssafn == nil { - return nil - } - return &fnVisitor{v.m, v.f, v.pkg, ssafn} - case nil: - return nil - default: - v.m[node] = v.ssafn - return v - } -} |