 bf27481efd
			
		
	
	
		bf27481efd
		
			
		
	
	
	
	
		
			
			full diff: https://github.com/dominikh/go-tools/compare/2019.2.3...2020.1.3 Also updates tests to accomodate updated rules: --- FAIL: TestSourcesFromTestdataWithIssuesDir/staticcheck.go (0.43s) linters_test.go:137: [run --disable-all --print-issued-lines=false --print-linter-name=false --out-format=line-number --max-same-issues=10 -Estaticcheck --no-config testdata/staticcheck.go] linters_test.go:33: Error Trace: linters_test.go:33 linters_test.go:138 linters_test.go:53 Error: Received unexpected error: staticcheck.go:11: no match for `self-assignment of x to x` vs ["SA4006: this value of `x` is never used"] in: staticcheck.go:11:2: SA4006: this value of `x` is never used unmatched errors staticcheck.go:11:2: SA4006: this value of `x` is never used Test: TestSourcesFromTestdataWithIssuesDir/staticcheck.go Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			185 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package report
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"go/ast"
 | |
| 	"go/printer"
 | |
| 	"go/token"
 | |
| 	"strings"
 | |
| 
 | |
| 	"golang.org/x/tools/go/analysis"
 | |
| 	"golang.org/x/tools/go/ast/astutil"
 | |
| 	"honnef.co/go/tools/facts"
 | |
| 	"honnef.co/go/tools/lint"
 | |
| )
 | |
| 
 | |
| type Options struct {
 | |
| 	ShortRange      bool
 | |
| 	FilterGenerated bool
 | |
| 	Fixes           []analysis.SuggestedFix
 | |
| 	Related         []analysis.RelatedInformation
 | |
| }
 | |
| 
 | |
| type Option func(*Options)
 | |
| 
 | |
| func ShortRange() Option {
 | |
| 	return func(opts *Options) {
 | |
| 		opts.ShortRange = true
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func FilterGenerated() Option {
 | |
| 	return func(opts *Options) {
 | |
| 		opts.FilterGenerated = true
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Fixes(fixes ...analysis.SuggestedFix) Option {
 | |
| 	return func(opts *Options) {
 | |
| 		opts.Fixes = append(opts.Fixes, fixes...)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Related(node Positioner, message string) Option {
 | |
| 	return func(opts *Options) {
 | |
| 		pos, end := getRange(node, opts.ShortRange)
 | |
| 		r := analysis.RelatedInformation{
 | |
| 			Pos:     pos,
 | |
| 			End:     end,
 | |
| 			Message: message,
 | |
| 		}
 | |
| 		opts.Related = append(opts.Related, r)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type Positioner interface {
 | |
| 	Pos() token.Pos
 | |
| }
 | |
| 
 | |
| type fullPositioner interface {
 | |
| 	Pos() token.Pos
 | |
| 	End() token.Pos
 | |
| }
 | |
| 
 | |
| type sourcer interface {
 | |
| 	Source() ast.Node
 | |
| }
 | |
| 
 | |
| // shortRange returns the position and end of the main component of an
 | |
| // AST node. For nodes that have no body, the short range is identical
 | |
| // to the node's Pos and End. For nodes that do have a body, the short
 | |
| // range excludes the body.
 | |
| func shortRange(node ast.Node) (pos, end token.Pos) {
 | |
| 	switch node := node.(type) {
 | |
| 	case *ast.File:
 | |
| 		return node.Pos(), node.Name.End()
 | |
| 	case *ast.CaseClause:
 | |
| 		return node.Pos(), node.Colon + 1
 | |
| 	case *ast.CommClause:
 | |
| 		return node.Pos(), node.Colon + 1
 | |
| 	case *ast.DeferStmt:
 | |
| 		return node.Pos(), node.Defer + token.Pos(len("defer"))
 | |
| 	case *ast.ExprStmt:
 | |
| 		return shortRange(node.X)
 | |
| 	case *ast.ForStmt:
 | |
| 		if node.Post != nil {
 | |
| 			return node.For, node.Post.End()
 | |
| 		} else if node.Cond != nil {
 | |
| 			return node.For, node.Cond.End()
 | |
| 		} else if node.Init != nil {
 | |
| 			// +1 to catch the semicolon, for gofmt'ed code
 | |
| 			return node.Pos(), node.Init.End() + 1
 | |
| 		} else {
 | |
| 			return node.Pos(), node.For + token.Pos(len("for"))
 | |
| 		}
 | |
| 	case *ast.FuncDecl:
 | |
| 		return node.Pos(), node.Type.End()
 | |
| 	case *ast.FuncLit:
 | |
| 		return node.Pos(), node.Type.End()
 | |
| 	case *ast.GoStmt:
 | |
| 		if _, ok := astutil.Unparen(node.Call.Fun).(*ast.FuncLit); ok {
 | |
| 			return node.Pos(), node.Go + token.Pos(len("go"))
 | |
| 		} else {
 | |
| 			return node.Pos(), node.End()
 | |
| 		}
 | |
| 	case *ast.IfStmt:
 | |
| 		return node.Pos(), node.Cond.End()
 | |
| 	case *ast.RangeStmt:
 | |
| 		return node.Pos(), node.X.End()
 | |
| 	case *ast.SelectStmt:
 | |
| 		return node.Pos(), node.Pos() + token.Pos(len("select"))
 | |
| 	case *ast.SwitchStmt:
 | |
| 		if node.Tag != nil {
 | |
| 			return node.Pos(), node.Tag.End()
 | |
| 		} else if node.Init != nil {
 | |
| 			// +1 to catch the semicolon, for gofmt'ed code
 | |
| 			return node.Pos(), node.Init.End() + 1
 | |
| 		} else {
 | |
| 			return node.Pos(), node.Pos() + token.Pos(len("switch"))
 | |
| 		}
 | |
| 	case *ast.TypeSwitchStmt:
 | |
| 		return node.Pos(), node.Assign.End()
 | |
| 	default:
 | |
| 		return node.Pos(), node.End()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getRange(node Positioner, short bool) (pos, end token.Pos) {
 | |
| 	switch node := node.(type) {
 | |
| 	case sourcer:
 | |
| 		s := node.Source()
 | |
| 		if short {
 | |
| 			return shortRange(s)
 | |
| 		}
 | |
| 		return s.Pos(), s.End()
 | |
| 	case fullPositioner:
 | |
| 		if short {
 | |
| 			return shortRange(node)
 | |
| 		}
 | |
| 		return node.Pos(), node.End()
 | |
| 	default:
 | |
| 		return node.Pos(), token.NoPos
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func Report(pass *analysis.Pass, node Positioner, message string, opts ...Option) {
 | |
| 	cfg := &Options{}
 | |
| 	for _, opt := range opts {
 | |
| 		opt(cfg)
 | |
| 	}
 | |
| 
 | |
| 	file := lint.DisplayPosition(pass.Fset, node.Pos()).Filename
 | |
| 	if cfg.FilterGenerated {
 | |
| 		m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
 | |
| 		if _, ok := m[file]; ok {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	pos, end := getRange(node, cfg.ShortRange)
 | |
| 	d := analysis.Diagnostic{
 | |
| 		Pos:            pos,
 | |
| 		End:            end,
 | |
| 		Message:        message,
 | |
| 		SuggestedFixes: cfg.Fixes,
 | |
| 		Related:        cfg.Related,
 | |
| 	}
 | |
| 	pass.Report(d)
 | |
| }
 | |
| 
 | |
| func Render(pass *analysis.Pass, x interface{}) string {
 | |
| 	var buf bytes.Buffer
 | |
| 	if err := printer.Fprint(&buf, pass.Fset, x); err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| func RenderArgs(pass *analysis.Pass, args []ast.Expr) string {
 | |
| 	var ss []string
 | |
| 	for _, arg := range args {
 | |
| 		ss = append(ss, Render(pass, arg))
 | |
| 	}
 | |
| 	return strings.Join(ss, ", ")
 | |
| }
 |