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>
		
			
				
	
	
		
			72 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			72 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package sharedcheck
 | 
						|
 | 
						|
import (
 | 
						|
	"go/ast"
 | 
						|
	"go/types"
 | 
						|
 | 
						|
	"golang.org/x/tools/go/analysis"
 | 
						|
	"honnef.co/go/tools/code"
 | 
						|
	"honnef.co/go/tools/internal/passes/buildir"
 | 
						|
	"honnef.co/go/tools/ir"
 | 
						|
	. "honnef.co/go/tools/lint/lintdsl"
 | 
						|
)
 | 
						|
 | 
						|
func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
 | 
						|
	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
 | 
						|
		cb := func(node ast.Node) bool {
 | 
						|
			rng, ok := node.(*ast.RangeStmt)
 | 
						|
			if !ok || !code.IsBlank(rng.Key) {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
 | 
						|
			v, _ := fn.ValueForExpr(rng.X)
 | 
						|
 | 
						|
			// Check that we're converting from string to []rune
 | 
						|
			val, _ := v.(*ir.Convert)
 | 
						|
			if val == nil {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
			Tsrc, ok := val.X.Type().(*types.Basic)
 | 
						|
			if !ok || Tsrc.Kind() != types.String {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
			Tdst, ok := val.Type().(*types.Slice)
 | 
						|
			if !ok {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
			TdstElem, ok := Tdst.Elem().(*types.Basic)
 | 
						|
			if !ok || TdstElem.Kind() != types.Int32 {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
 | 
						|
			// Check that the result of the conversion is only used to
 | 
						|
			// range over
 | 
						|
			refs := val.Referrers()
 | 
						|
			if refs == nil {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
 | 
						|
			// Expect two refs: one for obtaining the length of the slice,
 | 
						|
			// one for accessing the elements
 | 
						|
			if len(code.FilterDebug(*refs)) != 2 {
 | 
						|
				// TODO(dh): right now, we check that only one place
 | 
						|
				// refers to our slice. This will miss cases such as
 | 
						|
				// ranging over the slice twice. Ideally, we'd ensure that
 | 
						|
				// the slice is only used for ranging over (without
 | 
						|
				// accessing the key), but that is harder to do because in
 | 
						|
				// IR form, ranging over a slice looks like an ordinary
 | 
						|
				// loop with index increments and slice accesses. We'd
 | 
						|
				// have to look at the associated AST node to check that
 | 
						|
				// it's a range statement.
 | 
						|
				return true
 | 
						|
			}
 | 
						|
 | 
						|
			pass.Reportf(rng.Pos(), "should range over string, not []rune(string)")
 | 
						|
 | 
						|
			return true
 | 
						|
		}
 | 
						|
		Inspect(fn.Source(), cb)
 | 
						|
	}
 | 
						|
	return nil, nil
 | 
						|
}
 |