<commit_before>// Package simple contains a linter for Go source code.
package simple // import "honnef.co/go/simple"

import (
	"go/ast"
	"go/constant"
	"go/token"
	"go/types"
	"math"
	"reflect"
	"strconv"
	"strings"

	"honnef.co/go/lint"
)

var Funcs = map[string]lint.Func{
	"S1000": LintSingleCaseSelect,
	"S1001": LintLoopCopy,
	"S1002": LintIfBoolCmp,
	"S1003": LintStringsContains,
	"S1004": LintBytesCompare,
	"S1005": LintRanges,
	"S1006": LintForTrue,
	"S1007": LintRegexpRaw,
	"S1008": LintIfReturn,
	"S1009": LintRedundantNilCheckWithLen,
	"S1010": LintSlicing,
	"S1011": LintLoopAppend,
	"S1012": LintTimeSince,
	"S1013": LintSimplerReturn,
	"S1014": LintReceiveIntoBlank,
	"S1015": LintFormatInt,
	"S1016": LintSimplerStructConversion,
	"S1017": LintTrim,
}

type Checker struct{}

func NewChecker() *Checker                     { return &Checker{} }
func (c *Checker) Init(*lint.Program)          {}
func (c *Checker) Funcs() map[string]lint.Func { return Funcs }

func LintSingleCaseSelect(f *lint.File) {
	isSingleSelect := func(node ast.Node) bool {
		v, ok := node.(*ast.SelectStmt)
		if !ok {
			return false
		}
		return len(v.Body.List) == 1
	}

	seen := map[ast.Node]struct{}{}
	f.Walk(func(node ast.Node) bool {
		switch v := node.(type) {
		case *ast.ForStmt:
			if len(v.Body.List) != 1 {
				return true
			}
			if !isSingleSelect(v.Body.List[0]) {
				return true
			}
			if _, ok := v.Body.List[0].(*ast.SelectStmt).Body.List[0].(*ast.CommClause).Comm.(*ast.SendStmt); ok {
				// Don't suggest using range for channel sends
				return true
			}
			seen[v.Body.List[0]] = struct{}{}
			f.Errorf(node, "should use for range instead of for { select {} }")
		case *ast.SelectStmt:
			if _, ok := seen[v]; ok {
				return true
			}
			if !isSingleSelect(v) {
				return true
			}
			f.Errorf(node, "should use a simple channel send/receive instead of select with a single case")
			return true
		}
		return true
	})
}

func LintLoopCopy(f *lint.File) {
	fn := func(node ast.Node) bool {
		loop, ok := node.(*ast.RangeStmt)
		if !ok {
			return true
		}

		if loop.Key == nil {
			return true
		}
		if len(loop.Body.List) != 1 {
			return true
		}
		stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
		if !ok {
			return true
		}
		if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
			return true
		}
		lhs, ok := stmt.Lhs[0].(*ast.IndexExpr)
		if !ok {
			return true
		}
		if _, ok := f.Pkg.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok {
			return true
		}
		lidx, ok := lhs.Index.(*ast.Ident)
		if !ok {
			return true
		}
		key, ok := loop.Key.(*ast.Ident)
		if !ok {
			return true
		}
		if f.Pkg.TypesInfo.TypeOf(lhs) == nil || f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0]) == nil {
			return true
		}
		if f.Pkg.TypesInfo.ObjectOf(lidx) != f.Pkg.TypesInfo.ObjectOf(key) {
			return true
		}
		if !types.Identical(f.Pkg.TypesInfo.TypeOf(lhs), f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0])) {
			return true
		}
		if _, ok := f.Pkg.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok {
			return true
		}

		if rhs, ok := stmt.Rhs[0].(*ast.IndexExpr); ok {
			rx, ok := rhs.X.(*ast.Ident)
			_ = rx
			if !ok {
				return true
			}
			ridx, ok := rhs.Index.(*ast.Ident)
			if !ok {
				return true
			}
			if f.Pkg.TypesInfo.ObjectOf(ridx) != f.Pkg.TypesInfo.ObjectOf(key) {
				return true
			}
		} else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok {
			value, ok := loop.Value.(*ast.Ident)
			if !ok {
				return true
			}
			if f.Pkg.TypesInfo.ObjectOf(rhs) != f.Pkg.TypesInfo.ObjectOf(value) {
				return true
			}
		} else {
			return true
		}
		f.Errorf(loop, "should use copy() instead of a loop")
		return true
	}
	f.Walk(fn)
}

func LintIfBoolCmp(f *lint.File) {
	fn := func(node ast.Node) bool {
		expr, ok := node.(*ast.BinaryExpr)
		if !ok || (expr.Op != token.EQL && expr.Op != token.NEQ) {
			return true
		}
		x := f.IsBoolConst(expr.X)
		y := f.IsBoolConst(expr.Y)
		if x || y {
			var other ast.Expr
			var val bool
			if x {
				val = f.BoolConst(expr.X)
				other = expr.Y
			} else {
				val = f.BoolConst(expr.Y)
				other = expr.X
			}
			op := ""
			if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {
				op = "!"
			}
			f.Errorf(expr, "should omit comparison to bool constant, can be simplified to %s%s",
				op, f.Render(other))
		}
		return true
	}
	f.Walk(fn)
}

func LintStringsContains(f *lint.File) {
	// map of value to token to bool value
	allowed := map[string]map[token.Token]bool{
		"-1": {token.GTR: true, token.NEQ: true, token.EQL: false},
		"0":  {token.GEQ: true, token.LSS: false},
	}
	fn := func(node ast.Node) bool {
		expr, ok := node.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		switch expr.Op {
		case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:
		default:
			return true<commit_msg>Restructure code<commit_after>// Package simple contains a linter for Go source code.
package simple // import "honnef.co/go/simple"

import (
	"go/ast"
	"go/constant"
	"go/token"
	"go/types"
	"math"
	"reflect"
	"strconv"
	"strings"

	"honnef.co/go/lint"
)

var Funcs = map[string]lint.Func{
	"S1000": LintSingleCaseSelect,
	"S1001": LintLoopCopy,
	"S1002": LintIfBoolCmp,
	"S1003": LintStringsContains,
	"S1004": LintBytesCompare,
	"S1005": LintRanges,
	"S1006": LintForTrue,
	"S1007": LintRegexpRaw,
	"S1008": LintIfReturn,
	"S1009": LintRedundantNilCheckWithLen,
	"S1010": LintSlicing,
	"S1011": LintLoopAppend,
	"S1012": LintTimeSince,
	"S1013": LintSimplerReturn,
	"S1014": LintReceiveIntoBlank,
	"S1015": LintFormatInt,
	"S1016": LintSimplerStructConversion,
	"S1017": LintTrim,
}

type Checker struct{}

func NewChecker() *Checker                     { return &Checker{} }
func (c *Checker) Init(*lint.Program)          {}
func (c *Checker) Funcs() map[string]lint.Func { return Funcs }

func LintSingleCaseSelect(f *lint.File) {
	isSingleSelect := func(node ast.Node) bool {
		v, ok := node.(*ast.SelectStmt)
		if !ok {
			return false
		}
		return len(v.Body.List) == 1
	}

	seen := map[ast.Node]struct{}{}
	f.Walk(func(node ast.Node) bool {
		switch v := node.(type) {
		case *ast.ForStmt:
			if len(v.Body.List) != 1 {
				return true
			}
			if !isSingleSelect(v.Body.List[0]) {
				return true
			}
			if _, ok := v.Body.List[0].(*ast.SelectStmt).Body.List[0].(*ast.CommClause).Comm.(*ast.SendStmt); ok {
				// Don't suggest using range for channel sends
				return true
			}
			seen[v.Body.List[0]] = struct{}{}
			f.Errorf(node, "should use for range instead of for { select {} }")
		case *ast.SelectStmt:
			if _, ok := seen[v]; ok {
				return true
			}
			if !isSingleSelect(v) {
				return true
			}
			f.Errorf(node, "should use a simple channel send/receive instead of select with a single case")
			return true
		}
		return true
	})
}

func LintLoopCopy(f *lint.File) {
	fn := func(node ast.Node) bool {
		loop, ok := node.(*ast.RangeStmt)
		if !ok {
			return true
		}

		if loop.Key == nil {
			return true
		}
		if len(loop.Body.List) != 1 {
			return true
		}
		stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
		if !ok {
			return true
		}
		if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
			return true
		}
		lhs, ok := stmt.Lhs[0].(*ast.IndexExpr)
		if !ok {
			return true
		}
		if _, ok := f.Pkg.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok {
			return true
		}
		lidx, ok := lhs.Index.(*ast.Ident)
		if !ok {
			return true
		}
		key, ok := loop.Key.(*ast.Ident)
		if !ok {
			return true
		}
		if f.Pkg.TypesInfo.TypeOf(lhs) == nil || f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0]) == nil {
			return true
		}
		if f.Pkg.TypesInfo.ObjectOf(lidx) != f.Pkg.TypesInfo.ObjectOf(key) {
			return true
		}
		if !types.Identical(f.Pkg.TypesInfo.TypeOf(lhs), f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0])) {
			return true
		}
		if _, ok := f.Pkg.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok {
			return true
		}

		if rhs, ok := stmt.Rhs[0].(*ast.IndexExpr); ok {
			rx, ok := rhs.X.(*ast.Ident)
			_ = rx
			if !ok {
				return true
			}
			ridx, ok := rhs.Index.(*ast.Ident)
			if !ok {
				return true
			}
			if f.Pkg.TypesInfo.ObjectOf(ridx) != f.Pkg.TypesInfo.ObjectOf(key) {
				return true
			}
		} else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok {
			value, ok := loop.Value.(*ast.Ident)
			if !ok {
				return true
			}
			if f.Pkg.TypesInfo.ObjectOf(rhs) != f.Pkg.TypesInfo.ObjectOf(value) {
				return true
			}
		} else {
			return true
		}
		f.Errorf(loop, "should use copy() instead of a loop")
		return true
	}
	f.Walk(fn)
}

func LintIfBoolCmp(f *lint.File) {
	fn := func(node ast.Node) bool {
		expr, ok := node.(*ast.BinaryExpr)
		if !ok || (expr.Op != token.EQL && expr.Op != token.NEQ) {
			return true
		}
		x := f.IsBoolConst(expr.X)
		y := f.IsBoolConst(expr.Y)
		if !x && !y {
			return true
		}
		var other ast.Expr
		var val bool
		if x {
			val = f.BoolConst(expr.X)
			other = expr.Y
		} else {
			val = f.BoolConst(expr.Y)
			other = expr.X
		}
		op := ""
		if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {
			op = "!"
		}
		f.Errorf(expr, "should omit comparison to bool constant, can be simplified to %s%s",
			op, f.Render(other))
		return true
	}
	f.Walk(fn)
}

func LintStringsContains(f *lint.File) {
	// map of value to token to bool value
	allowed := map[string]map[token.Token]bool{
		"-1": {token.GTR: true, token.NEQ: true, token.EQL: false},
		"0":  {token.GEQ: true, token.LSS: false},
	}
	fn := func(node ast.Node) bool {
		expr, ok := node.(*ast.BinaryExpr)
		if !ok {
			return true
		}
		switch expr.Op {
		case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:
		default:
			return true
{'fraction_non_alphanumeric': 0.16647519582245432, 'fraction_numerical': 0.018590078328981723, 'mean_word_length': 2.9245901639344263, 'pattern_counts': {'":': 40, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '863102', 'n_tokens_mistral': 4427, 'n_tokens_neox': 3894, 'n_words': 870}