package rule import ( "fmt" "github.com/mgechev/revive/lint" "go/ast" "go/token" ) // RedefinesBuiltinIDRule warns when a builtin identifier is shadowed. type RedefinesBuiltinIDRule struct{} // Apply applies the rule to given file. func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure var builtInConstAndVars = map[string]bool{ "true": true, "false": true, "iota": true, "nil": true, } var builtFunctions = map[string]bool{ "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, } var builtInTypes = map[string]bool{ "ComplexType": true, "FloatType": true, "IntegerType": true, "Type": true, "Type1": true, "bool": true, "byte": true, "complex128": true, "complex64": true, "error": true, "float32": true, "float64": true, "int": true, "int16": true, "int32": true, "int64": true, "int8": true, "rune": true, "string": true, "uint": true, "uint16": true, "uint32": true, "uint64": true, "uint8": true, "uintptr": true, } onFailure := func(failure lint.Failure) { failures = append(failures, failure) } astFile := file.AST w := &lintRedefinesBuiltinID{builtInConstAndVars, builtFunctions, builtInTypes, onFailure} ast.Walk(w, astFile) return failures } // Name returns the rule name. func (r *RedefinesBuiltinIDRule) Name() string { return "redefines-builtin-id" } type lintRedefinesBuiltinID struct { constsAndVars map[string]bool funcs map[string]bool types map[string]bool onFailure func(lint.Failure) } func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.GenDecl: if n.Tok != token.TYPE { return nil // skip if not type declaration } typeSpec, ok := n.Specs[0].(*ast.TypeSpec) if !ok { return nil } id := typeSpec.Name.Name if w.types[id] { w.addFailure(n, fmt.Sprintf("redefinition of the built-in type %s", id)) } case *ast.FuncDecl: if n.Recv != nil { return w // skip methods } id := n.Name.Name if w.funcs[id] { w.addFailure(n, fmt.Sprintf("redefinition of the built-in function %s", id)) } case *ast.AssignStmt: for _, e := range n.Lhs { id, ok := e.(*ast.Ident) if !ok { continue } if w.constsAndVars[id.Name] { var msg string if n.Tok == token.DEFINE { msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name) } else { msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name) } w.addFailure(n, msg) } } } return w } func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) { w.onFailure(lint.Failure{ Confidence: 1, Node: node, Category: "logic", Failure: msg, }) }