Repository: mgechev/revive (go)

Issue:
Request: treat min, max and clear as built-in functions
**Is your feature request related to a problem? Please describe.**

go 1.21 has introduced new functions `min`, `max` and `clear`.
https://go.dev/ref/spec#Go_1.21

Currently revive does not treat `min`, `max` and `clear` as built-in functions,
https://github.com/mgechev/revive/blob/master/rule/redefines-builtin-id.go#L18-L34
so it does not report `redefinition of the built-in function` error.

```golang
// Reproduce issue
package main

import "fmt"

func main() {
	min := 0
	fmt.Println(min)
}
```

```
❯ revive -set_exit_status ./...

❯ echo $?
0
```

**Describe the solution you'd like**

revive treats `min`, `max` and `clear` as built-in functions on go 1.21+,
and reports the following error:
```
main.go: redefinition of the built-in function min
```

**Describe alternatives you've considered**

There may be an option not to make changes in order to maintain backward compatibility.

**Additional context**

NA


Pull request description:
check clear/max/min in redefines-builtin-id rule on go1.21+
Close #1021 

go1.21 has introduced new functions `clear`, `max` and `min`.
It would be nice revive checks these functions in redefines-builtin-id rule on go1.21+.

<!-- ### IMPORTANT ### -->
<!-- Please do not create a Pull Request without creating an issue first.** -->
<!-- If you're fixing a typo or improving the documentation, you may not have to open an issue. -->

<!-- ### CHECKLIST ### -->
<!-- Please, describe in details what's your motivation for this PR -->
<!-- Did you add tests? -->
<!-- Does your code follow the coding style of the rest of the repository? -->
<!-- Does the Travis build passes? -->

<!-- ### FOOTER (OPTIONAL) ### -->
<!-- If you're closing an issue, add "Closes #XXXX" in your comment. This way, the PR will be linked to the issue automatically. -->


Patch:
diff --git a/lint/package.go b/lint/package.go
index b4a0a72..441e0f4 100644
--- a/lint/package.go
+++ b/lint/package.go
@@ -33,6 +33,7 @@ var (
 	falseValue = 2
 	notSet     = 3
 
+	go121 = goversion.Must(goversion.NewVersion("1.21"))
 	go122 = goversion.Must(goversion.NewVersion("1.22"))
 )
 
@@ -194,6 +195,11 @@ func (p *Package) lint(rules []Rule, config Config, failures chan Failure) {
 	wg.Wait()
 }
 
+// IsAtLeastGo121 returns true if the Go version for this package is 1.21 or higher, false otherwise
+func (p *Package) IsAtLeastGo121() bool {
+	return p.goVersion.GreaterThanOrEqual(go121)
+}
+
 // IsAtLeastGo122 returns true if the Go version for this package is 1.22 or higher, false otherwise
 func (p *Package) IsAtLeastGo122() bool {
 	return p.goVersion.GreaterThanOrEqual(go122)
diff --git a/rule/redefines-builtin-id.go b/rule/redefines-builtin-id.go
index b3ff084..f367ea8 100644
--- a/rule/redefines-builtin-id.go
+++ b/rule/redefines-builtin-id.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"go/ast"
 	"go/token"
+	"maps"
 
 	"github.com/mgechev/revive/lint"
 )
@@ -33,6 +34,12 @@ var builtFunctions = map[string]bool{
 	"recover": true,
 }
 
+var builtFunctionsAfterGo121 = map[string]bool{
+	"clear": true,
+	"max":   true,
+	"min":   true,
+}
+
 var builtInTypes = map[string]bool{
 	"bool":       true,
 	"byte":       true,
@@ -69,7 +76,17 @@ func (*RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.F
 	}
 
 	astFile := file.AST
-	w := &lintRedefinesBuiltinID{onFailure}
+
+	builtFuncs := maps.Clone(builtFunctions)
+	if file.Pkg.IsAtLeastGo121() {
+		maps.Copy(builtFuncs, builtFunctionsAfterGo121)
+	}
+	w := &lintRedefinesBuiltinID{
+		onFailure:           onFailure,
+		builtInConstAndVars: builtInConstAndVars,
+		builtFunctions:      builtFuncs,
+		builtInTypes:        builtInTypes,
+	}
 	ast.Walk(w, astFile)
 
 	return failures
@@ -81,7 +98,10 @@ func (*RedefinesBuiltinIDRule) Name() string {
 }
 
 type lintRedefinesBuiltinID struct {
-	onFailure func(lint.Failure)
+	onFailure           func(lint.Failure)
+	builtInConstAndVars map[string]bool
+	builtFunctions      map[string]bool
+	builtInTypes        map[string]bool
 }
 
 func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor {
@@ -162,16 +182,16 @@ func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) {
 	})
 }
 
-func (lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) {
-	if builtFunctions[id] {
+func (w *lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) {
+	if w.builtFunctions[id] {
 		return true, "function"
 	}
 
-	if builtInConstAndVars[id] {
+	if w.builtInConstAndVars[id] {
 		return true, "constant or variable"
 	}
 
-	if builtInTypes[id] {
+	if w.builtInTypes[id] {
 		return true, "type"
 	}
 


Test patch:
diff --git a/testdata/redefines-builtin-id.go b/testdata/redefines-builtin-id.go
index 22e5975..e5f7c16 100644
--- a/testdata/redefines-builtin-id.go
+++ b/testdata/redefines-builtin-id.go
@@ -32,3 +32,12 @@ var i, copy int // MATCH /redefinition of the built-in function copy/
 
 // issue #792
 type ()
+
+func foo() {
+	clear := 0 // MATCH /redefinition of the built-in function clear/
+	max := 0   // MATCH /redefinition of the built-in function max/
+	min := 0   // MATCH /redefinition of the built-in function min/
+	_ = clear
+	_ = max
+	_ = min
+}